簡単にできるのでご紹介します。
判定したい画像と、間違いの画像を複数保有している時に
これらを学習させて
モデルを作成し、
そのモデルを使って新しく判定したい画像をpostすると
判定結果が出るというようなコードがあると色々な現場に役立ちそうですよね。
そこで、
ラーメンの画像がたくさんと
ラーメンじゃない画像がたくさんあるときに
それを学習させて、
新しく画像を投げた時にそれがラーメンかどうか判定するコードを紹介します。
ラーメンでは無くて例えば
不良品の野菜判定とか、食品のカビ判定とか、金属のヒビ判定とか
そういうのに使えます。
まずファイルツリー構造は
ファイルツリー構造
deeplearning/
├── Dockerfile
├── docker-compose.yml
├── data/
│ ├── ramen/ ← ラーメン画像を置く
│ └── not_ramen/ ← ラーメンではない画像を置く
└── app/
├── app.py ← Flask サーバー(判定用API)
├── train_model.py ← モデル学習スクリプト
├── requirements.txt
├── templates/
│ ├── index.html ← 画像アップロードフォーム
│ └── result.html ← 判定結果表示ページ
└── static/uploads/ ← アップロード画像保存先
それぞれのコードを紹介します。
Dockerfile
FROM python:3.10-slim
WORKDIR /app
# 依存ライブラリをインストール
COPY app/requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir -r /app/requirements.txt
# アプリとデータをコピー
COPY app /app
COPY data /data
EXPOSE 8080
# モデルがなければ自動学習→Flask起動
CMD ["bash", "-c", "if [ ! -f model/ramen_model.h5 ]; then python train_model.py; fi && python app.py"]
docker-compose.yml
version: '3'
services:
ramen_ai:
build: .
container_name: ramen_ai_app
ports:
- "8080:8080"
volumes:
- ./data:/data
- ./app/model:/app/model
- ./app/static/uploads:/app/static/uploads
app/requirements.txt
flask
tensorflow
pillow
numpy
app/train_model.py
(完全版:壊れた画像スキップ+クリーンデータ学習)
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten
from PIL import Image, UnidentifiedImageError
import shutil
import os
import pathlib
RAW_DIR = "/data"
CLEAN_DIR = "/tmp/clean_data"
MODEL_PATH = "model/ramen_model.h5"
# ===========================
# クリーンデータセット作成
# ===========================
def prepare_clean_dataset(raw_dir, clean_dir):
print("🔍 画像チェック&コピー中...")
if os.path.exists(clean_dir):
shutil.rmtree(clean_dir)
os.makedirs(clean_dir, exist_ok=True)
for cls_name in ["ramen", "not_ramen"]:
src_dir = pathlib.Path(raw_dir) / cls_name
dst_dir = pathlib.Path(clean_dir) / cls_name
os.makedirs(dst_dir, exist_ok=True)
for path in src_dir.glob("*"):
if path.suffix.lower() not in [".jpg", ".jpeg", ".png"]:
print(f"🗑️ 非画像ファイルスキップ: {path.name}")
continue
try:
with Image.open(path) as im:
im.verify()
shutil.copy(path, dst_dir / path.name)
except UnidentifiedImageError:
print(f"⚠️ 壊れた画像スキップ: {path.name}")
except Exception as e:
print(f"⚠️ 読み込みエラー: {path.name} ({e})")
print("✅ クリーンデータセット作成完了:", clean_dir)
# ===========================
# モデル学習
# ===========================
def train_model():
os.makedirs("model", exist_ok=True)
if os.path.exists(MODEL_PATH):
print("✅ 既存モデルがあります。再学習をスキップします。")
return
prepare_clean_dataset(RAW_DIR, CLEAN_DIR)
datagen = ImageDataGenerator(
rescale=1.0/255,
validation_split=0.2,
rotation_range=20,
zoom_range=0.2,
horizontal_flip=True
)
train_gen = datagen.flow_from_directory(
CLEAN_DIR,
target_size=(150, 150),
batch_size=8,
class_mode="binary",
subset="training"
)
val_gen = datagen.flow_from_directory(
CLEAN_DIR,
target_size=(150, 150),
batch_size=8,
class_mode="binary",
subset="validation"
)
model = Sequential([
Flatten(input_shape=(150, 150, 3)),
Dense(128, activation="relu"),
Dense(1, activation="sigmoid")
])
model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"])
print("🧠 学習開始...")
model.fit(train_gen, validation_data=val_gen, epochs=5)
model.save(MODEL_PATH)
print("✅ モデルを保存しました:", MODEL_PATH)
if __name__ == "__main__":
train_model()
app/app.py
from flask import Flask, render_template, request
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing import image
import numpy as np
import os
app = Flask(__name__)
MODEL_PATH = "model/ramen_model.h5"
if not os.path.exists(MODEL_PATH):
raise FileNotFoundError("モデルが存在しません。train_model.pyで学習を行ってください。")
model = load_model(MODEL_PATH)
UPLOAD_FOLDER = "static/uploads"
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
@app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
file = request.files["file"]
if not file:
return "ファイルがありません"
filepath = os.path.join(UPLOAD_FOLDER, file.filename)
file.save(filepath)
img = image.load_img(filepath, target_size=(150, 150))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0) / 255.0
pred = model.predict(x)[0][0]
prob = round(float(pred), 3)
threshold = 0.4 # ← 閾値調整可能
result = f"確率: {prob} → {'🍜ラーメンです' if prob > threshold else '🙅♂️ラーメンではないです'}"
return render_template("result.html", result=result, image=file.filename)
return render_template("index.html")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
app/templates/index.html 画面のコード
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ラーメン画像判定</title>
</head>
<body>
<h1>🍥 ラーメン画像をアップロードしてください</h1>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="file" accept="image/*" required>
<button type="submit">判定する</button>
</form>
</body>
</html>
app/templates/result.html 結果HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>判定結果</title>
</head>
<body>
<h1>{{ result }}</h1>
<img src="{{ url_for('static', filename='uploads/' + image) }}" width="300">
<br><a href="/">戻る</a>
</body>
</html>
そしてモデル学習を実行してFlask実行
docker-compose run ramen_ai python train_model.py
docker-compose up -d
そしたら、
127.0.0.1:8080でアクセスできます。

💻 コード解説
train_model.py
(モデル学習)
docker-compose run ramen_ai python train_model.py
を実行すると、data/
以下の画像を読み込み、自動的にラーメン判定モデル(app/model/ramen_model.h5
)を作成します。
ログには以下のように出力されます:
🔍 画像チェック&コピー中...
⚠️ 壊れた画像スキップ: IMG_6290.jpg
✅ クリーンデータセット作成完了: /tmp/clean_data
Epoch 1/5 ...
✅ モデルを保存しました: model/ramen_model.h5
app.py
(推論・判定)
Flask アプリが立ち上がり、ブラウザからアクセスできます。
docker-compose up -d
アクセスURL:
http://127.0.0.1:8080
画像をアップロードすると、学習済みモデルを使って
「🍜ラーメンです」または「🙅♂️ラーメンではないです」
の結果が即時に返ります。
🧠 使い方の流れ
data/ramen
にラーメン画像、data/not_ramen
にラーメンでない画像を配置。- モデルを学習:
docker-compose run ramen_ai python train_model.py
- Flaskサーバーを起動:
docker-compose up -d
- ブラウザでアクセスして判定!
⚙️ データが少ない場合の設定変更
学習データが少ない場合は、データ拡張(augmentation)を有効にします。train_model.py
のこの部分を変更👇
train_datagen = ImageDataGenerator(
rescale=1.0/255,
validation_split=0.2,
rotation_range=20,
zoom_range=0.2,
horizontal_flip=True,
)
これで少ない画像でもモデルがより多様な特徴を学習できます。
🚀 精度を上げたい場合
もし画像枚数が少ないけど精度を上げたいなら、転移学習を使いましょう。
train_model.py
のモデル部分を以下のように差し替えるだけです👇
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.models import Model
from tensorflow.keras.layers import GlobalAveragePooling2D, Dense
base = MobileNetV2(weights='imagenet', include_top=False, input_shape=(150,150,3))
x = base.output
x = GlobalAveragePooling2D()(x)
x = Dense(64, activation='relu')(x)
output = Dense(1, activation='sigmoid')(x)
model = Model(inputs=base.input, outputs=output)
for layer in base.layers:
layer.trainable = False # 転移学習の基本
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
→ これで、少数データでも精度が格段に向上します。
🎚 判定を“緩く”したい場合
Flask の判定ロジック(app.py
)で、
出力確率の閾値を変更します👇
pred = model.predict(x)[0][0]
result = "ラーメンです 🍜" if pred > 0.4 else "ラーメンではないです 🙅♂️"
0.5
→ 0.4
のように下げると「ラーメン」と判定しやすくなります。
実際に動かしてみて動いているコードなので
何かの参考になるのではないかと思います。