はじめに
2025年、何をやったかなと振り返ってみたところ、
そういえばボディビル大会に出場しました。
大会は見事に予選落ちだったのですが、
以下のデータがあったため、折角なので分析してみようと思います。
- 減量期間は約1年間
- 体重データ(ほぼ毎日)
- 自撮り写真(ほぼ毎日)
やること
- ローカルLLM Ollamaを使用し、1年間のデータを評価させる
- Ollamaの採用理由
- 使ってみたかったから(重要)
- 画像解析をトライアンドエラーでやるのもAPIだとコストがかかりそう
- ChatGPT や Gemini に数百枚の自撮り写真を見てもらうのも恥ずかしい
全体の流れ
┌─────────────────────┐
│ Step1: 絶対評価 │ ← 1日単位の画像1枚の評価と体重データ
└─────────────────────┘
↓
┌─────────────────────┐
│ Step2: 相対評価 │ ← 時系列で傾向分析
└─────────────────────┘
↓
┌─────────────────────┐
│ Step3: 総合評価 │
└─────────────────────┘
Step1:Ollama(ローカル)で画像を絶対評価
モデルの準備
モデルもたくさんありますが、
スペック的にリーズナブルで、画像解析に向いているモデルを使用します。
ollama pull llava:7b
Step1. 絶対評価項目(6つ)
出力は以下の6項目に絞りました。
-
shoulder(肩幅) -
chest(胸部) -
waist(ウエスト) -
legs(脚部) -
bodyfat(見た目の脂肪) -
posture(姿勢)
各項目は「狭い / 普通 / やや広い / 広い」など、
決め打ちのラベルから1つだけ選ぶ 形にして、後処理しやすくしています。
画像バッチ解析(Python)
数百枚を手で投げるのはさすがに無理なので、
Python から Ollama の HTTP API を叩いて一括処理します。
ざっくり構成はこんな感じです:
# analyze_images.py (イメージ)
MODEL = "llava:7b"
API_URL = "http://localhost:11434/api/generate"
HEADER = ["filename", "shoulder", "chest", "waist", "legs", "bodyfat", "posture"]
PROMPT = """
以下のJSON形式のみで出力してください。説明文は禁止です。
{
"shoulder": "",
"chest": "",
"waist": "",
"legs": "",
"bodyfat": "",
"posture": ""
}
各項目は次の候補から1つだけ選んで記入してください。
- shoulder: 狭い / 普通 / やや広い / 広い
- chest: 発達していない / 普通 / やや発達 / 発達している
- waist: 細い / 普通 / やや太い / 太い
- legs: 細い / 普通 / やや発達 / 発達している
- bodyfat: 少ない / 普通 / やや多い / 多い
- posture: 良い / 普通 / やや悪い / 悪い
"""
def analyze_image(path: Path) -> dict:
img_b64 = base64.b64encode(path.read_bytes()).decode()
payload = {
"model": MODEL,
"prompt": PROMPT,
"images": [img_b64],
"stream": False,
}
res = requests.post(API_URL, json=payload, timeout=120)
res.raise_for_status()
raw = res.json()["response"].strip()
return json.loads(json_str)
def main():
with OUT_CSV.open("a", newline="") as f:
writer = csv.writer(f)
for img in files:
result = analyze_image(img)
writer.writerow([
img.name,
result.get("shoulder", ""),
result.get("chest", ""),
result.get("waist", ""),
result.get("legs", ""),
result.get("bodyfat", ""),
result.get("posture", ""),
])
このスクリプトを流すと、最終的にこんなCSVができます:
filename,shoulder,chest,waist,legs,bodyfat,posture
0001.jpg,普通,発達している,太い,多い,多い,悪い
0002.jpg,普通,発達している,太い,普通,多い,良い
...
これで 「各画像を、6つのラベルで記述したデータセット」 が手に入りました。
Step2. 相対評価
ヘルスケア連携の体重計アプリから、
日付と体重データをCSVとしてエクスポートしました。
Step1のCSVに体重情報をマージします。
最終的なCSVはこんな形になります:
```csv
filename,photo_date,weight_kg,shoulder,chest,waist,legs,bodyfat,posture
0001.jpg,2024-03-12,77.9,普通,普通,普通,普通,普通,普通
0002.jpg,2024-03-13,77.7,普通,発達している,やや太い,発達している,多い,悪い
:
0227.jpg,2025-02-14,59.4,普通,発達している,細い,発達している,少ない,良い
0228.jpg,2025-02-14,59.4,普通,発達している,普通,発達している,少ない,良い
0229.jpg,2025-02-15,59.6,普通,普通,普通,普通,普通,一時的に不自然
0230.jpg,2025-02-16,58.8,普通,発達している,細い,細い,少ない,良い
Step3. 総合評価
ここまでで、
- 画像から抽出した 6 つの見た目ラベル(絶対評価)
- 体重データと日付(相対評価)
が 1 つの CSV にまとまりました。
ここからは、この CSV を複数のローカル LLM に渡して、
「1 年間の変化を総合的に振り返る」パートです。
使用したモデル
今回は、Ollama 上の次の 3 モデルに同じ CSV を読ませて比較しました。
| モデル | 特徴 |
|---|---|
| phi3:mini | 超軽量で高速。だが長文要約は破綻しやすい |
| phi3:14b | 安定して分析可能。自宅PCでも実用レベル |
| mixtral:8x7b | 高性能 MoE モデル |
今回の CSV を複数のローカル LLM に渡してみると、モデルごとの「得意・不得意」が明確に分かれました。
phi3:mini
非常に軽量で応答が速い反面、長い CSV を保持しながら複数指標の関係性をまとめる処理が苦手で、時系列の整合性が崩れる場面が多く見られた。
軽量モデルでは小さいトークン処理は得意だが、複数日のデータを跨ぐような大きい推論は限界がありそう。
文脈が崩れて誤字が出たり、別言語が混じることもあり、長文処理の限界が見える場面もありました。
(出力結果抜粋)
3月27日(初期体重: 55kg)から 2024年2月6日(初期体重: 57kg)、体重が逆転している。
13月4日日本語での推移:体重が55kgから57kgへの減少
緊急事由上、刷り下げ待ちのフェーズを迎え...
phi3:14b
mini より安定しており、体重の増減やラベルの傾向はある程度捉えられるものの、要約が若干揺れる。
ローカルPCでも十分扱えるサイズで、簡易的な分析には向いている。
(出力結果抜粋)
体重推移: 「発達期」(2025-06-16から2025-06-23)、「停滞期」(2025-07-21〜2025-08-03)、「増加点」(2025-09-07以降)
細い腹回りは偏頭痛の改善が見られる
学び: 定期的な停滞がありますが、長期的には体重は徐々に減少しています。
mixtral:8x7b
性能的には期待していたのですが、PC スペックが足りずフリーズしてしまい、今回は分析に使うことができませんでした。
総じて、ローカル LLM の画像ラベル化(Step1)は非常に有効でしたが、
「1 年間のデータを統合して総合評価する」というタスクは、
モデル規模やコンテキスト保持能力の差が結果に強く反映されることが分かりました。
総評
1 年分のラベルと体重を時系列で統合してみると、体型変化には明確な流れがありました。
体重が70kgを切る頃から waist と bodyfat が「細い / 少ない」方向へ安定して移行し、
一方で chest・legs の「発達している」は体重最小期でも維持されており、
筋量をあまり落とさずに減量できていたようです。
また、姿勢(posture)は体重より遅れて改善し、停滞に見える時期でも
姿勢や筋量ラベルが動いており、単なる「減らない期間」ではなく
身体が調整しているフェーズが存在していることが確認できました。
全体として、減量は単に数字が下がるだけではなく、
“落ちる指標・維持される指標・遅れて変化する指標” が時期ごとに分かれていることが分かり、
画像ラベルと体重を統合することで、その構造がより明確に見えるようになりました。
まとめ
1 年間のデータをこうして振り返ってみると、
自身が想定していた以上に 身体は変化していた ことが分かりました。
数値だけでは気づけなかった改善や停滞、
写真だけでは見えなかった傾向もあり、今回の分析は有意義な振り返りとなりました。
今後、もっと他のLLMも使いこなしてみたいです。
あと、ChatGPT や Gemini などが動作するリソース(マシンパワー)ってすごい
