Skip to content

付録. 機械学習アプリの作り方

本書ではこれまで、データの前処理からモデルの構築・評価、画像処理、自然言語処理、深層学習まで、機械学習の技術を学んできました。本付録では、これらの技術を活かして実際に動く機械学習アプリケーションを構築する方法を学びます。学習済みモデルの保存・再利用から、推論処理の作成、UI の構築、そしてインターネット上への公開(デプロイ)までを段階的に進めていきます。感情分析アプリに加えて、ChatGPT 風のチャットアプリも作ります。

Web アプリケーションの概要

Web アプリケーションとは、Web ブラウザを通じて利用できるアプリケーションのことです。ユーザは特別なソフトウェアをインストールすることなく、ブラウザさえあればどこからでもアプリケーションを利用できます。

Web アプリケーションの利点

  • アクセスの容易さ: ブラウザがあればどこからでもアクセス可能
  • クロスプラットフォーム: Windows、Mac、スマートフォンなど、環境を選ばない
  • 共有が簡単: URL を共有するだけで他の人も利用可能
  • メンテナンスが楽: サーバ側を更新すれば全ユーザに反映

機械学習モデルの Web アプリ化

機械学習モデルを Web アプリとして公開することで、以下のようなことが可能になります。

  • 実用的なサービス: モデルを実際のサービスとして提供
  • デモンストレーション: 研究成果やポートフォリオとして公開
  • API として提供: 他のシステムから利用可能

Python の Web フレームワーク

Python には、Web アプリケーションを構築するためのフレームワークがいくつか存在します。

フレームワーク 特徴 適用場面
Streamlit 簡単、素早くプロトタイプを作成 デモ、社内ツール
Flask 軽量、柔軟性が高い 小〜中規模の Web アプリ、API
FastAPI 高速、API 構築に特化 REST API、本格的なサービス
Django 多機能、大規模開発向け 大規模 Web アプリケーション

本付録では、モデルの保存(A.1)と推論処理の作成(A.2)を済ませたうえで、Flask を使った Web アプリ・REST API の実装(A.3)、Streamlit を使った UI の実装(A.4)、公開(A.5)までを順に紹介します。書籍の付録で示した 4 つの手順「モデルの保存 → 予測処理の作成 → 画面(UI)の作成 → 公開(デプロイ)」は、それぞれ A.1・A.2・A.4・A.5 に対応します。

A.1 モデルの保存と再利用

プログラミングの知識がないユーザでもブラウザから簡単に利用できる Web アプリや、他のシステムから呼び出せる API を構築するには、まず学習済みモデルをファイルに保存し、必要なときに読み込めるようにしておく必要があります。モデルの保存は、機械学習モデルをアプリケーションとして公開するための第一歩です。

これまでの章では、コードを実行するたびにモデルを学習していました。しかし、Web アプリではアプリの起動時に学習済みモデルを読み込んで予測に使用します。モデルの学習には時間がかかることもあるため、あらかじめ学習を済ませてファイルに保存しておき、必要なときに読み込む仕組みが必要です。

A.1.1 pickle によるモデルの保存と読み込み

Python の標準ライブラリ pickle を使うと、学習済みのモデルをファイルに保存(シリアライズ)し、後から読み込む(デシリアライズ)ことができます。

import pickle
import numpy as np
import seaborn as sns
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split

# モデルの学習(iris は seaborn 版で読み込み)
df_iris = sns.load_dataset('iris')
feature_cols = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
X_train, X_test, y_train, y_test = train_test_split(
    df_iris[feature_cols].values, df_iris['species'],
    test_size=0.2, random_state=42
)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)

# モデルをファイルに保存
with open('iris_model.pkl', 'wb') as f:
    pickle.dump(model, f)

保存したモデルを読み込んで、予測に使用します。

# モデルの読み込み
with open('iris_model.pkl', 'rb') as f:
    loaded_model = pickle.load(f)

# 読み込んだモデルで予測
sample = np.array([[5.1, 3.5, 1.4, 0.2]])
prediction = loaded_model.predict(sample)
print(f"予測結果: {prediction[0]}")  # 予測結果: setosa

pickle.dump() 関数でオブジェクトをバイナリ形式でファイルに書き出し、pickle.load() 関数でファイルからオブジェクトを復元します。ファイルを開くときのモードは、書き出し時が 'wb'(write binary)、読み込み時が 'rb'(read binary)です。

A.1.2 joblib による保存

scikit-learn のモデル保存には、joblib ライブラリも広く使われています。joblib は NumPy 配列を含むオブジェクトの保存に最適化されており、大きなモデルでは pickle よりも効率的です。

import joblib

# モデルの保存
joblib.dump(model, 'iris_model.joblib')

# モデルの読み込み
loaded_model = joblib.load('iris_model.joblib')

pickle と joblib のどちらを使っても問題ありませんが、大きなモデルの扱いには joblib が向いています。

A.1.3 パイプラインの保存

第 5 章で学んだパイプライン(前処理とモデルを一体化したもの)も、同じ方法で保存・読み込みができます。パイプラインごと保存すれば、前処理とモデルをまとめて管理できるため、本番環境での利用に便利です。学習済みパイプライン pipeline があれば、モデルと同じく joblib で保存・読み込みできます。

A.2 推論処理の作り方

保存したモデルを読み込み、入力に学習時と同じ前処理を適用して予測結果を返す処理が推論処理です(書籍の付録では予測処理と呼んでいます)。基本的な流れは「モデルの読み込み → 入力データの前処理 → 予測の実行 → 結果の返却」で、これを 1 つの関数にまとめておくと、Web アプリや API から同じ処理をそのまま再利用できます。ここでは、第 7 章でも使ったペンギンデータセットで分類モデルを学習し、保存してから推論関数を作ります。

A.2.1 予測用モデルの準備

まず、推論処理で使うモデルを学習して保存します。

# train_penguin.py
import seaborn as sns
import joblib
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier

# データの準備(推論時は NumPy 配列を渡すため、.values で学習しておく)
penguins = sns.load_dataset("penguins").dropna()
X = penguins[["bill_length_mm", "bill_depth_mm",
              "flipper_length_mm", "body_mass_g"]].values
y = penguins["species"]

# パイプラインの構築と学習
pipeline = Pipeline([
    ("scaler", StandardScaler()),
    ("classifier", RandomForestClassifier(
        n_estimators=100, random_state=42
    ))
])
pipeline.fit(X, y)

# モデルの保存
joblib.dump(pipeline, "penguin_model.joblib")
print("モデルを保存しました")
print(f"クラス: {list(pipeline.classes_)}")
モデルを保存しました
クラス: ['Adelie', 'Chinstrap', 'Gentoo']

A.2.2 推論関数の作成

次に、保存したモデルを読み込んで推論関数を作ります。時間のかかるモデルの読み込みは関数の外で 1 回だけ行い、関数は「特徴量を受け取り、予測結果を返す」ことに専念させるのがポイントです。

# predict.py
import joblib
import numpy as np

# モデルの読み込み(アプリ起動時に1回だけ実行する想定)
model = joblib.load("penguin_model.joblib")

# 特徴量からペンギンの種類と各クラスの確率を返す
def predict_penguin(bill_length, bill_depth,
                    flipper_length, body_mass):
    features = np.array([[bill_length, bill_depth,
                          flipper_length, body_mass]])
    prediction = model.predict(features)[0]
    probabilities = model.predict_proba(features)[0]
    proba_dict = {
        species: round(float(prob), 4)
        for species, prob in zip(model.classes_, probabilities)
    }
    return prediction, proba_dict

# 動作確認
species, proba = predict_penguin(39.1, 18.7, 181.0, 3750.0)
print(f"予測結果: {species}")
print(f"確率: {proba}")
予測結果: Adelie
確率: {'Adelie': 1.0, 'Chinstrap': 0.0, 'Gentoo': 0.0}

このように推論処理を関数として切り出しておくと、次節以降の Web アプリや API から同じ処理を呼び出せます。

A.3 Flask による Web アプリと REST API

ここまでで、学習済みモデルの保存と推論関数ができました。本節では、軽量な Web フレームワーク Flask を使って、推論処理を Web アプリや REST API(他のプログラムから HTTP 経由で予測を呼び出せる仕組み)として公開する方法を学びます。A.4 で Streamlit を使って作る感情分析アプリと同じ「LM Studio サーバ + 分類器」の構成を、REST API・HTML フォームとして実装します。API を「使う側」の一般的なパターンは第14章を参照してください。まず画面のあるアプリを作りたい人は、本節を飛ばして A.4 に進んでも構いません。

A.3.1 Flask とは

Flask は、Python で Web アプリケーションを構築するための軽量なフレームワークです。Streamlit よりも柔軟性が高く、より本格的な Web アプリや REST API を構築できます。

Flask の特徴

  • 軽量: 必要最小限の機能のみを提供
  • 柔軟: 拡張性が高く、カスタマイズしやすい
  • REST API: API サーバとして利用できる
  • 豊富なエコシステム: 多数の拡張ライブラリが存在

A.3.2 Flask のインストール

Flask は CHIKUWA Editor に標準では含まれていないため、ターミナルで以下のコマンドを実行してインストールします(自分で構築したローカル環境でも同様です)。

pip install flask

A.3.3 Flask の基本的な使い方

最も簡単な Flask アプリの例を見てみましょう。

hello_flask.py
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello, Flask!"

if __name__ == '__main__':
    app.run(debug=True)

このスクリプトを実行します。

python hello_flask.py

ブラウザで http://localhost:5000 にアクセスすると、"Hello, Flask!" と表示されます。

A.3.4 Flask の基本構造

Flask アプリは、ルーティング(URL とハンドラ関数の対応付け)を基本としています。

from flask import Flask, request, render_template

app = Flask(__name__)

# ルート(トップページ)
@app.route('/')
def index():
    return "トップページ"

# 別のページ
@app.route('/about')
def about():
    return "About ページ"

# パラメータを受け取る
@app.route('/user/<name>')
def user(name):
    return f"Hello, {name}!"

# POST リクエストを処理
@app.route('/submit', methods=['POST'])
def submit():
    data = request.form['data']
    return f"受け取ったデータ: {data}"

if __name__ == '__main__':
    app.run(debug=True)

A.3.5 テンプレートエンジン

Flask では、HTML テンプレートを使って動的なページを生成できます。テンプレートには Jinja2 というテンプレートエンジンを使用します。

ディレクトリ構造

project/
├── app.py
└── templates/
    └── index.html

テンプレート(templates/index.html)

<!DOCTYPE html>
<html>
  <head>
    <title>{{ title }}</title>
  </head>
  <body>
    <h1>{{ message }}</h1>
  </body>
</html>

Flask アプリ(app.py)

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html',
                           title="My App",
                           message="Hello, Flask!")

if __name__ == '__main__':
    app.run(debug=True)

A.3.6 REST API とは

REST API は、HTTP リクエスト/レスポンスを使ってデータをやり取りする仕組みです。Web アプリや他のプログラムから機械学習モデルを利用できるようになります。機械学習の文脈では、予測したいデータを JSON 形式で送信し、予測結果を JSON 形式で受け取るのが一般的です。

主な HTTP メソッドは以下のとおりです。

  • GET:データの取得(例:Web ページの表示)
  • POST:データの送信(例:予測リクエスト)

REST API の利点

  • プログラムから利用可能: 他のアプリケーションから呼び出せる
  • 言語非依存: どんなプログラミング言語からでも利用可能
  • スケーラブル: サーバを増やして処理能力を向上できる

A.3.7 感情分析 API の実装

Flask アプリ自身が「LM Studio サーバ + 分類器」をラップして、別の REST API として公開する形にします。クライアントから見ると Flask アプリだけが見え、内部でローカル LLM サーバを使っている事実は隠蔽されます。分類器には、A.4.6 で保存する sentiment_clf.pkl を使います。LM Studio サーバを使わず、書籍第 13 章のモデルで作りたい場合は、encode_one() 関数 と分類器の組を A.4.9 の predict_sentiment() 関数 に置き換えてください。

graph LR
    A("クライアント<br/>curl, Python") -->|POST /predict<br/>text| B("Flask API")
    B -->|POST /v1/embeddings| C("LM Studio サーバ<br/>bge-m3")
    C -->|1024 次元ベクトル| B
    B --> D("分類器<br/>pickle")
    D --> B
    B -->|sentiment, confidence| A

    classDef proc fill:#D9F0EA,stroke:#009E73,stroke-width:2px,color:#000
    classDef model fill:#F7EBF2,stroke:#CC79A7,stroke-width:2px,color:#000
    classDef neu fill:#F2F2F2,stroke:#8C8C8C,stroke-width:2px,color:#000
    class A neu
    class B proc
    class C,D model
api.py
from flask import Flask, request, jsonify
import requests
import numpy as np
import pickle

LLM_URL = "https://xxxxxxxx.trycloudflare.com"  # 授業でアナウンスされる URL に置き換える
EMBED_MODEL = "text-embedding-bge-m3"

app = Flask(__name__)

# 分類器の読み込み
print("分類器を読み込んでいます...")
with open('sentiment_clf.pkl', 'rb') as f:
    classifier = pickle.load(f)

print("分類器の読み込みが完了しました。")

# テキスト 1 文を LM Studio でベクトル化
def encode_one(text):
    r = requests.post(
        f"{LLM_URL}/v1/embeddings",
        json={"model": EMBED_MODEL, "input": text},
        timeout=30,
    )
    return np.array([r.json()["data"][0]["embedding"]])

@app.route('/')
def index():
    return """
    <h1>感情分析 API</h1>
    <p>使い方:POST /predict にテキストを送信してください。</p>
    <p>例:curl -X POST -H "Content-Type: application/json" -d '{"text":"この映画は面白かった"}' http://localhost:5000/predict</p>
    """

@app.route('/predict', methods=['POST'])
def predict():
    data = request.get_json()

    if 'text' not in data:
        return jsonify({'error': 'テキストが指定されていません'}), 400

    text = data['text']
    if not text.strip():
        return jsonify({'error': 'テキストが空です'}), 400

    # LM Studio でベクトル化
    embedding = encode_one(text)

    # 予測
    prediction = classifier.predict(embedding)[0]
    probability = classifier.predict_proba(embedding)[0]

    result = {
        'text': text,
        'sentiment': 'positive' if prediction == 1 else 'negative',
        'confidence': float(probability[prediction]),
        'probabilities': {
            'positive': float(probability[1]),
            'negative': float(probability[0]),
        },
    }

    return jsonify(result)

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)

このアプリは「API を 使う側 (LM Studio を叩く)」と「API を 作る側 (/predict を公開する)」の両方を同時に体験できる構成になっています。API 呼び出しの一般的なパターンは第14章で詳しく扱っています。

A.3.8 API の使い方

API の起動

python api.py

curl コマンドでテスト

curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"text":"この映画は本当に素晴らしかった!"}' \
  http://localhost:5000/predict

レスポンス例

{
  "text": "この映画は本当に素晴らしかった!",
  "sentiment": "positive",
  "confidence": 0.92,
  "probabilities": {
    "positive": 0.92,
    "negative": 0.08
  }
}

Python からの利用

import requests

url = 'http://localhost:5000/predict'
data = {'text': 'この映画はつまらなかった'}

response = requests.post(url, json=data)
result = response.json()

print(result)

A.3.9 Flask アプリと HTML フォーム

Flask アプリに HTML フォームを追加して、ブラウザから直接利用できるようにしてみましょう。

ディレクトリ構造

project/
├── app.py
├── sentiment_clf.pkl
└── templates/
    ├── index.html
    └── result.html

app.py

from flask import Flask, render_template, request
import requests
import numpy as np
import pickle

LLM_URL = "https://xxxxxxxx.trycloudflare.com"  # 授業でアナウンスされる URL に置き換える
EMBED_MODEL = "text-embedding-bge-m3"

app = Flask(__name__)

# 分類器の読み込み
with open('sentiment_clf.pkl', 'rb') as f:
    classifier = pickle.load(f)

def encode_one(text):
    r = requests.post(
        f"{LLM_URL}/v1/embeddings",
        json={"model": EMBED_MODEL, "input": text},
        timeout=30,
    )
    return np.array([r.json()["data"][0]["embedding"]])

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/analyze', methods=['POST'])
def analyze():
    text = request.form['text']

    if not text.strip():
        return render_template('index.html', error="テキストを入力してください")

    # LM Studio でベクトル化と予測
    embedding = encode_one(text)
    prediction = classifier.predict(embedding)[0]
    probability = classifier.predict_proba(embedding)[0]

    sentiment = 'Positive' if prediction == 1 else 'Negative'
    confidence = probability[prediction]

    return render_template('result.html',
                           text=text,
                           sentiment=sentiment,
                           confidence=confidence,
                           prob_positive=probability[1],
                           prob_negative=probability[0])

if __name__ == '__main__':
    app.run(debug=True)

templates/index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>感情分析アプリ</title>
    <style>
      body {
        font-family: Arial, sans-serif;
        max-width: 600px;
        margin: 50px auto;
        padding: 20px;
      }
      textarea {
        width: 100%;
        height: 150px;
        padding: 10px;
        font-size: 14px;
      }
      button {
        background-color: #4caf50;
        color: white;
        padding: 10px 20px;
        border: none;
        cursor: pointer;
        font-size: 16px;
      }
      button:hover {
        background-color: #45a049;
      }
      .error {
        color: red;
      }
    </style>
  </head>
  <body>
    <h1>😊 感情分析アプリ</h1>
    <p>テキストを入力すると、ポジティブかネガティブかを判定します。</p>

    {% if error %}
    <p class="error">{{ error }}</p>
    {% endif %}

    <form method="POST" action="/analyze">
      <textarea
        name="text"
        placeholder="テキストを入力してください..."
      ></textarea>
      <br /><br />
      <button type="submit">分析する</button>
    </form>
  </body>
</html>

templates/result.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>分析結果</title>
    <style>
      body {
        font-family: Arial, sans-serif;
        max-width: 600px;
        margin: 50px auto;
        padding: 20px;
      }
      .result {
        padding: 20px;
        border-radius: 5px;
        margin: 20px 0;
      }
      .positive {
        background-color: #d4edda;
        border: 1px solid #c3e6cb;
      }
      .negative {
        background-color: #f8d7da;
        border: 1px solid #f5c6cb;
      }
      a {
        color: #007bff;
        text-decoration: none;
      }
    </style>
  </head>
  <body>
    <h1>分析結果</h1>

    <div
      class="result {% if sentiment == 'Positive' %}positive{% else %}negative{% endif %}"
    >
      <h2>{{ sentiment }}</h2>
      <p><strong>入力テキスト:</strong> {{ text }}</p>
      <p><strong>信頼度:</strong> {{ "%.2f"|format(confidence * 100) }}%</p>
      <hr />
      <p>Positive: {{ "%.2f"|format(prob_positive * 100) }}%</p>
      <p>Negative: {{ "%.2f"|format(prob_negative * 100) }}%</p>
    </div>

    <a href="/">← 戻る</a>
  </body>
</html>

A.4 UI の作り方

推論処理を構築したら、次はユーザが操作しやすいインタフェース(UI)を作成します。本節では、Streamlit を使って、Python だけでインタラクティブな機械学習アプリの UI を構築する方法を紹介します。

A.4.1 Streamlit とは

Streamlit は、データサイエンス向けの Web アプリを簡単に作成できる Python ライブラリです。わずか数行のコードで、インタラクティブな Web アプリを構築できます。

Streamlit の特徴

  • 簡単: Python スクリプトを書くだけで Web アプリが作れる
  • 高速: プロトタイプを素早く作成できる
  • インタラクティブ: スライダ、ボタンなどの UI 部品が豊富
  • ホットリロード: コードを編集すると自動的に反映される

A.4.2 Streamlit のインストール

CHIKUWA Editor には Streamlit が標準でインストールされているため、そのまま使えます。自分で構築したローカル環境では、以下のコマンドでインストールします。

pip install streamlit

クラウドのノートブック環境について

本付録は CHIKUWA Editor またはローカル環境での開発を想定しています。Google Colab などのクラウドのノートブック環境では、Web アプリの開発・実行に工夫が必要となります。

A.4.3 Streamlit の基本的な使い方

まず、簡単な Hello World アプリを作成してみましょう。

app.py
import streamlit as st

# タイトル
st.title("Hello, Streamlit!")

# テキスト
st.write("これは Streamlit で作った Web アプリです。")

# ボタン
if st.button("クリックしてください"):
    st.write("ボタンがクリックされました!")

# スライダー
value = st.slider("値を選択してください", 0, 100, 50)
st.write(f"選択された値: {value}")

CHIKUWA Editor では、通常のプログラムと同じように実行するだけでローカルサーバが立ち上がり、Web アプリが表示されます。その他の環境では、ターミナルで以下のコマンドを実行すると、ブラウザで Web アプリが開きます(通常は http://localhost:8501)。

streamlit run app.py

A.4.4 主要な Streamlit コンポーネント

Streamlit では、さまざまな UI コンポーネントを簡単に追加できます。

テキスト表示

st.title("タイトル")
st.header("ヘッダー")
st.subheader("サブヘッダー")
st.write("テキスト")
st.markdown("**太字** や *斜体* も使えます")

入力ウィジェット

# テキスト入力
text = st.text_input("テキストを入力してください")

# テキストエリア
long_text = st.text_area("長いテキストを入力してください")

# 数値入力
number = st.number_input("数値を入力してください", min_value=0, max_value=100)

# スライダー
value = st.slider("値を選択してください", 0, 100, 50)

# セレクトボックス
option = st.selectbox("選択してください", ["オプション1", "オプション2", "オプション3"])

# ボタン
if st.button("送信"):
    st.write("送信されました!")

データ表示

import pandas as pd

# DataFrame の表示
df = pd.DataFrame({
    "列1": [1, 2, 3],
    "列2": [4, 5, 6]
})
st.dataframe(df)

# テーブルの表示
st.table(df)

グラフ表示

import matplotlib.pyplot as plt

# Matplotlib のグラフ
fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [1, 4, 2, 3])
st.pyplot(fig)

# 簡易グラフ
st.line_chart(df)
st.bar_chart(df)

公式ドキュメント: Streamlit API Reference

A.4.5 感情分析アプリの概要

第 13 章の補足「授業用ローカル LLM サーバを使う場合」で扱った「ローカル LLM サーバ(LM Studio)+ ロジスティック回帰」の組み合わせで、テキストの感情分析を行う Web アプリを作成します。サーバの接続情報や、自分の PC で LM Studio を起動する方法もその補足にまとめてあります。サーバを使わず、書籍第 13 章の TF-IDF + 分類器で作る手順は A.4.9 で扱います。

機能

  • ユーザがテキストを入力
  • 「Positive(ポジティブ)」「Negative(ネガティブ)」を分類
  • 予測結果と確率を表示

全体構成

graph LR
    A("ブラウザ") -->|テキスト入力| B("Streamlit アプリ")
    B -->|HTTP POST<br/>/v1/embeddings| C("LM Studio サーバ<br/>bge-m3")
    C -->|1024 次元ベクトル| B
    B --> D("ロジスティック回帰<br/>事前学習済み")
    D --> B
    B -->|結果| A

    classDef proc fill:#D9F0EA,stroke:#009E73,stroke-width:2px,color:#000
    classDef model fill:#F7EBF2,stroke:#CC79A7,stroke-width:2px,color:#000
    classDef neu fill:#F2F2F2,stroke:#8C8C8C,stroke-width:2px,color:#000
    class A neu
    class B proc
    class C,D model

A.4.6 分類器の準備と保存

まず、LM Studio サーバから得た Embedding を使って分類器(ロジスティック回帰)だけを学習・保存しておきます。Embedding を取るモデル自体(bge-m3)はサーバ側に常駐しているので保存不要です。

import requests
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
import pickle

LLM_URL = "https://xxxxxxxx.trycloudflare.com"  # 授業でアナウンスされる URL に置き換える
EMBED_MODEL = "text-embedding-bge-m3"

def encode(texts):
    if isinstance(texts, str):
        texts = [texts]
    r = requests.post(
        f"{LLM_URL}/v1/embeddings",
        json={"model": EMBED_MODEL, "input": texts},
    )
    return np.array([d["embedding"] for d in r.json()["data"]])

# データの読み込み
df = pd.read_csv('movie_reviews.csv')

# LM Studio でベクトル化(少し時間がかかります)
print("ベクトル化中...")
embeddings = encode(df['text'].tolist())

# ラベルを数値に変換
df['label_num'] = (df['label'] == 'positive').astype(int)

# 訓練データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(
    embeddings, df['label_num'],
    test_size=0.2,
    random_state=42
)

# ロジスティック回帰で学習
clf = LogisticRegression(random_state=42, max_iter=1000)
clf.fit(X_train, y_train)

# 分類器のみ保存(Embedding モデルはサーバ側にあるので保存不要)
with open('sentiment_clf.pkl', 'wb') as f:
    pickle.dump(clf, f)

print("分類器を保存しました: sentiment_clf.pkl")

A.4.7 感情分析アプリの実装

保存した分類器を読み込み、入力テキストを LM Studio でベクトル化 → 分類する Streamlit アプリを作ります。

sentiment_app.py
import streamlit as st
import requests
import numpy as np
import pickle

LLM_URL = "https://xxxxxxxx.trycloudflare.com"  # 授業でアナウンスされる URL に置き換える
EMBED_MODEL = "text-embedding-bge-m3"

# ページの設定
st.set_page_config(page_title="感情分析アプリ", page_icon="😊")

# タイトル
st.title("😊 感情分析アプリ")
st.write("テキストを入力すると、Positive(ポジティブ)か Negative(ネガティブ)かを判定します。")

# 分類器の読み込み(キャッシュを使って 1 回だけ読み込む)
@st.cache_resource
def load_classifier():
    with open('sentiment_clf.pkl', 'rb') as f:
        return pickle.load(f)

classifier = load_classifier()

# 1 文を LM Studio でベクトル化
def encode_one(text):
    r = requests.post(
        f"{LLM_URL}/v1/embeddings",
        json={"model": EMBED_MODEL, "input": text},
    )
    return np.array([r.json()["data"][0]["embedding"]])

# テキスト入力
text_input = st.text_area(
    "テキストを入力してください",
    height=150,
    placeholder="例:この映画は本当に面白かった!"
)

# 予測ボタン
if st.button("感情を分析"):
    if text_input.strip() == "":
        st.warning("テキストを入力してください。")
    else:
        with st.spinner("LM Studio サーバで解析中..."):
            embedding = encode_one(text_input)
            prediction = classifier.predict(embedding)[0]
            probability = classifier.predict_proba(embedding)[0]

        # 結果の表示
        st.subheader("分析結果")

        if prediction == 1:
            st.success("😊 Positive(ポジティブ)")
            st.progress(probability[1])
            st.write(f"確率: {probability[1]:.2%}")
        else:
            st.error("😞 Negative(ネガティブ)")
            st.progress(probability[0])
            st.write(f"確率: {probability[0]:.2%}")

        with st.expander("詳細情報を表示"):
            st.write(f"Positive の確率: {probability[1]:.2%}")
            st.write(f"Negative の確率: {probability[0]:.2%}")

# サイドバー
st.sidebar.header("ℹ️ このアプリについて")
st.sidebar.write("""
このアプリは、機械学習を使って日本語テキストの感情を分析します。

**使用技術**
- ローカル LLM サーバ(LM Studio + bge-m3)
- Logistic Regression
- Streamlit
""")

A.4.8 感情分析アプリの実行

アプリを実行します。CHIKUWA Editor 以外の環境では、ターミナルで以下のコマンドを実行します。

streamlit run sentiment_app.py

A.4.9 書籍のモデルで作る場合(サーバ不要版)

A.4.6〜A.4.7 のアプリは LM Studio サーバに依存しています。サーバがない環境でも、書籍の第 13 章で作った「MeCab の分かち書き + TF-IDF + 分類器」をそのままアプリにできます。この構成なら Embedding モデルもサーバも不要で、必要なファイルは保存したパイプライン 1 つだけです。

まず、第 13 章と同じ映画レビューデータで、TF-IDF ベクトル化と分類器を 1 つのパイプラインにまとめて学習し、保存します。

train_sentiment.py
# train_sentiment.py
import pickle
import MeCab
import pandas as pd
from urllib.request import urlretrieve
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline

# データの読み込み(第13章と同じ映画レビュー)
url = "https://ml.kano.ac/chapters/data/movie_reviews.csv"
urlretrieve(url, "movie_reviews.csv")
df = pd.read_csv("movie_reviews.csv")

# MeCabで分かち書き(アプリ側でも入力に同じ処理を行う)
tagger = MeCab.Tagger("-Owakati")
df["wakati"] = [tagger.parse(text).strip() for text in df["text"]]

# ラベルを数値に変換(positive=1, negative=0)
df["label_num"] = (df["label"] == "positive").astype(int)

# 訓練データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(
    df["wakati"], df["label_num"], test_size=0.2, random_state=42
)

# TF-IDFベクトル化と分類器をパイプラインにまとめて学習
pipeline = Pipeline([
    ("tfidf", TfidfVectorizer()),
    ("classifier", LogisticRegression(max_iter=1000)),
])
pipeline.fit(X_train, y_train)
print(f"テスト精度: {pipeline.score(X_test, y_test):.2%}")

# パイプラインごと保存(ベクトル化と分類器がセットで保存される)
with open("sentiment_pipeline.pkl", "wb") as f:
    pickle.dump(pipeline, f)
print("保存しました: sentiment_pipeline.pkl")
テスト精度: 77.50%
保存しました: sentiment_pipeline.pkl

分かち書きをパイプラインの外に出している理由

第 13 章では TfidfVectorizer(tokenizer=tokenize) のように、分かち書きの関数をベクトル化器に渡していました。しかし pickle は関数を「名前」で保存するため、そのまま保存すると、読み込む側のプログラムに同じ名前の関数が定義されていないとエラーになります。ここでは分かち書きを前処理として先に済ませ、パイプラインには標準の TfidfVectorizer クラスを入れています。その代わり、アプリ側では入力文に 学習時と同じ分かち書き を行ってからパイプラインに渡します。

次に、保存したパイプラインを読み込む Streamlit アプリです。A.4.7 との違いは、encode_one() 関数 で LM Studio を呼ぶ代わりに、分かち書きしてパイプラインに渡す predict_sentiment() 関数 を使う点だけです。

sentiment_app_local.py
import streamlit as st
import MeCab
import pickle

# ページの設定
st.set_page_config(page_title="感情分析アプリ", page_icon="😊")

# タイトル
st.title("😊 感情分析アプリ(サーバ不要版)")
st.write("テキストを入力すると、Positive(ポジティブ)か Negative(ネガティブ)かを判定します。")

# パイプラインの読み込み(キャッシュを使って 1 回だけ読み込む)
@st.cache_resource
def load_pipeline():
    with open("sentiment_pipeline.pkl", "rb") as f:
        return pickle.load(f)

pipeline = load_pipeline()
tagger = MeCab.Tagger("-Owakati")

# 学習時と同じ前処理(分かち書き)をしてから予測する
def predict_sentiment(text):
    wakati = tagger.parse(text).strip()
    prediction = pipeline.predict([wakati])[0]
    probability = pipeline.predict_proba([wakati])[0]
    return prediction, probability

# テキスト入力
text_input = st.text_area(
    "テキストを入力してください",
    height=150,
    placeholder="例:この映画は本当に面白かった!"
)

# 予測ボタン
if st.button("感情を分析"):
    if text_input.strip() == "":
        st.warning("テキストを入力してください。")
    else:
        prediction, probability = predict_sentiment(text_input)

        # 結果の表示
        st.subheader("分析結果")
        if prediction == 1:
            st.success("😊 Positive(ポジティブ)")
            st.progress(probability[1])
            st.write(f"確率: {probability[1]:.2%}")
        else:
            st.error("😞 Negative(ネガティブ)")
            st.progress(probability[0])
            st.write(f"確率: {probability[0]:.2%}")
streamlit run sentiment_app_local.py

書籍の付録で示した 4 つの手順のうち、「モデルの保存」がパイプラインの保存、「予測処理の作成」が predict_sentiment() 関数、「画面(UI)の作成」がこのアプリに対応します。公開(デプロイ)の手順は A.5 で扱います。

A.4.10 キャッシュの活用

@st.cache_resource デコレータを使うことで、モデルの読み込みを 1 回だけ実行し、結果をキャッシュできます。これにより、アプリの起動が高速になります。

@st.cache_resource
def load_models():
    # 時間のかかる処理(モデルの読み込みなど)
    return model

キャッシュの種類:

デコレータ 用途
@st.cache_resource モデル、DB 接続などのグローバルなリソース
@st.cache_data データの読み込みや変換処理

続いて、ChatGPT 風のチャットアプリを作ります。

A.4.11 チャットモデルの呼び出し

第 13 章の補足と A.4.6 では、LM Studio サーバの Embedding API(/v1/embeddings)を使いました。同じサーバには日本語対応のチャットモデルも載っており、/v1/chat/completions エンドポイントを叩くと、ChatGPT のようなチャット形式の応答が得られます。

項目 内容
サーバ URL 授業でアナウンスされる URL(第 13 章の補足と同じサーバ)
チャットモデル qwen/qwen3.5-9b(日本語に強い 9B モデル)
API エンドポイント POST /v1/chat/completions (OpenAI 互換)

まずは requests で 1 回だけ呼び出してみます。

import requests

LLM_URL = "https://xxxxxxxx.trycloudflare.com"  # 授業でアナウンスされる URL に置き換える
CHAT_MODEL = "qwen/qwen3.5-9b"

response = requests.post(
    f"{LLM_URL}/v1/chat/completions",
    json={
        "model": CHAT_MODEL,
        "messages": [
            {"role": "system", "content": "あなたは親切なアシスタントです。"},
            {"role": "user", "content": "機械学習とは何ですか?簡潔に説明してください。"},
        ],
        "stream": False,
    },
)

result = response.json()
print(result["choices"][0]["message"]["content"])

ポイントは messages です。role には次の 3 種類があり、「これまでのやり取り」をリストとしてまるごと渡すのが LLM API の共通パターンです。

role 意味
system モデルへの指示(人格・口調・ルールなど)
user ユーザの発言
assistant モデルの過去の応答

モデル自身は会話を覚えていないため、会話を続けるには、過去のやり取り(user と assistant)を messages に追記して毎回すべて送り直します。外部 API(OpenAI など)の使い方や JSON 形式での構造化出力など、チャット API のより詳しい話は第14章を参照してください。

A.4.12 チャット UI と会話履歴

Streamlit にはチャット専用の UI 部品が用意されています。

もう 1 つの鍵が 会話履歴の保持 です。Streamlit のスクリプトは、ボタンを押すなどの操作のたびに先頭から再実行されるため、普通の変数は毎回初期化されてしまいます。操作をまたいで残したいデータは st.session_state 属性に保存します。

まずは LLM を使わない「オウム返し」で、この 3 つの部品の動きを確認してみましょう。

chat_ui_test.py
import streamlit as st

st.title("チャット UI の練習")

# 会話履歴の初期化(初回だけ実行される)
if "messages" not in st.session_state:
    st.session_state.messages = []

# 入力欄(Enter で送信)
prompt = st.chat_input("メッセージを入力してください")
if prompt:
    st.session_state.messages.append({"role": "user", "content": prompt})

# 履歴をすべて吹き出しで表示
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.write(message["content"])

メッセージを送るたびにスクリプト全体が再実行されますが、履歴は st.session_state.messages に残っているので、過去の発言が積み上がっていきます。

A.4.13 チャットアプリの実装

チャット UI と LM Studio のチャット API を組み合わせると、ChatGPT 風のアプリが完成します。

全体構成

graph LR
    A("ブラウザ") -->|メッセージ入力| B("Streamlit アプリ")
    B -->|POST /v1/chat/completions<br/>会話履歴全体| C("LM Studio サーバ<br/>qwen/qwen3.5-9b")
    C -->|応答テキスト| B
    B -->|履歴に追加して表示| A

    classDef proc fill:#D9F0EA,stroke:#009E73,stroke-width:2px,color:#000
    classDef model fill:#F7EBF2,stroke:#CC79A7,stroke-width:2px,color:#000
    classDef neu fill:#F2F2F2,stroke:#8C8C8C,stroke-width:2px,color:#000
    class A neu
    class B proc
    class C model
chat_app.py
import streamlit as st
import requests

LLM_URL = "https://xxxxxxxx.trycloudflare.com"  # 授業でアナウンスされる URL に置き換える
CHAT_MODEL = "qwen/qwen3.5-9b"
SYSTEM_PROMPT = "あなたは親切なアシスタントです。簡潔な日本語で回答してください。"

# ページの設定
st.set_page_config(page_title="チャットアプリ", page_icon="💬")
st.title("💬 チャットアプリ")

# 会話履歴の初期化(初回だけ実行される)
if "messages" not in st.session_state:
    st.session_state.messages = []

# これまでの会話履歴を表示
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.write(message["content"])

# ユーザの入力を受け取る
prompt = st.chat_input("メッセージを入力してください")
if prompt:
    # ユーザの発言を履歴に追加して表示
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.write(prompt)

    # システムプロンプト + 会話履歴全体を送って応答を取得
    with st.chat_message("assistant"):
        with st.spinner("考え中..."):
            response = requests.post(
                f"{LLM_URL}/v1/chat/completions",
                json={
                    "model": CHAT_MODEL,
                    "messages": [{"role": "system", "content": SYSTEM_PROMPT}]
                    + st.session_state.messages,
                    "stream": False,
                },
            )
            answer = response.json()["choices"][0]["message"]["content"]
        st.write(answer)

    # モデルの応答も履歴に追加
    st.session_state.messages.append({"role": "assistant", "content": answer})

A.4.14 チャットアプリの実行

アプリを実行します。CHIKUWA Editor 以外の環境では、ターミナルで以下のコマンドを実行します。

streamlit run chat_app.py

「昨日の話の続きだけど」のように前の発言を踏まえた質問をしても文脈が通じるのは、毎回 st.session_state.messages の履歴全体を送っているからです。裏を返せば、会話が長くなるほど送信するデータ(トークン)も増えていきます。実用的なチャットアプリでは、古い履歴を要約したり切り捨てたりする工夫が入っています。

ストリーミング表示(発展)

ChatGPT のように応答を 1 文字ずつ表示するには、リクエストで "stream": True を指定し、少しずつ届く応答を st.write_stream() 関数で描画します。レスポンスの受け取り方が変わるため少し難しくなりますが、興味のある人は挑戦してみてください。

A.5 デプロイの流れ

A.5.1 デプロイとは

デプロイ(Deploy)とは、作成したアプリケーションをインターネット上のサーバに配置し、誰でもアクセスできるようにすることです。

A.5.2 主要なデプロイ先

サービス 特徴 無料プラン
Streamlit Community Cloud Streamlit 専用、簡単 あり(制限付き)
Render Flask/FastAPI など幅広く対応 あり(制限付き)
Heroku 老舗、多機能 有料のみ
Google Cloud Run コンテナベース、スケーラブル 無料枠あり
AWS (EC2, Lambda) 柔軟性が高い、学習コストも高い 無料枠あり(1 年間)
自宅サーバ 自由度が最も高い サーバ費用が必要

A.5.3 Streamlit Community Cloud へのデプロイ

Streamlit アプリを最も簡単に公開する方法は、Streamlit Community Cloud を使うことです。

手順

  1. GitHub にコードをアップロード

    • リポジトリを作成
    • app.py、requirements.txt(必要なライブラリ)、モデルファイルをアップロード
  2. requirements.txt の作成

    requirements.txt
    streamlit
    requests
    numpy
    scikit-learn
    
  3. Streamlit Community Cloud に登録

  4. アプリをデプロイ

    • "New app" をクリック
    • GitHub リポジトリを選択
    • デプロイするファイル(app.py)を指定
    • "Deploy!" をクリック

数分でアプリが公開され、URL が発行されます。発行される URL は https://ユーザ名-アプリ名.streamlit.app のような形式で(形式は変わることがあります)、誰でもブラウザからアクセスできるようになります。

A.5.4 注意点

モデルファイルのサイズ

  • 大きなモデルファイルは GitHub にアップロードできません(100MB 制限)
  • 対処法: モデルの軽量化 / Git LFS の使用 / クラウドストレージから読み込む

環境変数

  • API キーなどの秘密情報は、環境変数として設定する
  • Streamlit では st.secrets 属性を使って安全に管理できる

依存ライブラリの指定

  • requirements.txt にすべての依存ライブラリを正確に記述する

セキュリティ

  • ユーザ入力を適切にバリデーションし、不正なデータによるエラーを防ぐ

バックエンドのサーバへの到達性

  • LM Studio サーバを使うアプリ(A.4.7 など)は、デプロイ先からサーバに到達できる必要があります
  • サーバ不要版(A.4.9)にはこの制約がありませんが、requirements.txt に mecab-python3 と unidic-lite を加える必要があります
  • trycloudflare の URL は毎回変わるため、公開運用では固定 URL のサーバや外部 API への差し替えが必要です

A.6 演習問題

演習 A-1: BMI 計算アプリ

演習 A-1(初級)

Streamlit の基本的な UI 部品だけを使って、BMI(体格指数)を計算する Web アプリを作成してください。

タスク

  1. st.number_input() 関数で身長(cm)と体重(kg)を入力できるようにする
  2. 「計算する」ボタンが押されたら BMI = 体重[kg] ÷ (身長[m])² を計算し、st.metric() 関数で表示する
  3. BMI が 18.5 未満なら「やせ型」、25 以上なら「肥満」、その間なら「標準」と表示する
テンプレートコード(bmi_app.py)
import streamlit as st

st.title("BMI 計算アプリ")

# 1. st.number_input で身長(cm)と体重(kg)の入力欄を作る

# 2. ボタンが押されたら BMI = 体重 ÷ (身長[m]) ** 2 を計算して st.metric で表示する

# 3. BMI の値に応じて「やせ型」「標準」「肥満」を表示する

演習 A-2: チャットアプリのキャラクタ切り替え

演習 A-2(中級)

A.4.13 で作った chat_app.py を改造して、サイドバーでアシスタントのキャラクタ(システムプロンプト)を切り替えられるようにしてください。

タスク

  1. キャラクタ名 → システムプロンプトの辞書を定義する(例:標準アシスタント/関西弁/猫)
  2. st.sidebar 属性の st.sidebar.selectbox() メソッドでキャラクタを選べるようにする
  3. キャラクタが変更されたら会話履歴(st.session_state.messages)をリセットする
  4. 選択中のキャラクタのシステムプロンプトを使って応答させ、口調が変わることを確認する
テンプレートコード(chat_app2.py)
import streamlit as st
import requests

LLM_URL = "https://xxxxxxxx.trycloudflare.com"  # 授業でアナウンスされる URL に置き換える
CHAT_MODEL = "qwen/qwen3.5-9b"

# 1. キャラクタごとのシステムプロンプトを辞書で定義する
CHARACTERS = {
    "標準アシスタント": "あなたは親切なアシスタントです。簡潔な日本語で回答してください。",
    # ここにキャラクタを追加する
}

st.set_page_config(page_title="チャットアプリ", page_icon="💬")
st.title("💬 チャットアプリ")

# 2. サイドバーの selectbox でキャラクタを選択できるようにする

# 3. キャラクタが変わったら会話履歴をリセットする

# 4. 以降は chat_app.py と同じ(システムプロンプトだけ選択中のものを使う)

演習 A-3: 感情分析アプリ + 履歴機能

演習 A-3(中級)

A.4 節で作った感情分析アプリに、判定結果の履歴機能を追加してください。LM Studio サーバが使えない場合は、A.4.9 のサーバ不要版をベースにしても構いません。

タスク

  1. st.session_state 属性に判定履歴のリストを初期化する
  2. 「感情を分析」ボタンで判定するたびに、テキスト・判定結果・Positive 確率を履歴に追加する
  3. 履歴を st.dataframe() 関数で一覧表示する
  4. 「履歴をクリア」ボタンで履歴を空にできるようにする
テンプレートコード(sentiment_app2.py)
import streamlit as st
import requests
import numpy as np
import pandas as pd
import pickle

LLM_URL = "https://xxxxxxxx.trycloudflare.com"  # 授業でアナウンスされる URL に置き換える
EMBED_MODEL = "text-embedding-bge-m3"

st.title("😊 感情分析アプリ(履歴付き)")

# 分類器の読み込み(A.4.6 で保存した sentiment_clf.pkl を使う)
@st.cache_resource
def load_classifier():
    with open('sentiment_clf.pkl', 'rb') as f:
        return pickle.load(f)

classifier = load_classifier()

# 1 文を LM Studio でベクトル化
def encode_one(text):
    r = requests.post(
        f"{LLM_URL}/v1/embeddings",
        json={"model": EMBED_MODEL, "input": text},
    )
    return np.array([r.json()["data"][0]["embedding"]])

# 1. st.session_state に判定履歴のリストを初期化する

text_input = st.text_area("テキストを入力してください", height=100)

if st.button("感情を分析"):
    # 2. ベクトル化 → 予測して結果を表示し、履歴に追加する(A.4.7 と同じ要領)
    pass

# 3. 履歴があれば st.dataframe で一覧表示する

# 4. 「履歴をクリア」ボタンを付ける

まとめ

本付録では、機械学習モデルを Web アプリケーションとして展開する方法について学びました。

付録のまとめ

Web アプリケーション

  • ブラウザさえあれば利用でき、URL の共有だけで他の人にも使ってもらえる
  • 機械学習モデルのデモ・サービス化の手段として有効
  • Python では Streamlit / Flask / FastAPI / Django などのフレームワークを使う

モデルの保存と再利用

  • 「学習」と「予測」を切り離すため、学習済みモデルを pickle / joblib でファイルに保存する
  • パイプラインごと保存すれば、前処理とモデルをまとめて管理できる
  • 書籍第 13 章の TF-IDF + 分類器も、分かち書きを前処理として揃えればパイプラインごと保存してそのままアプリにできる(A.4.9)

Flask / REST API

  • 軽量で柔軟な Web フレームワーク
  • ルーティングで URL とハンドラを対応付け
  • テンプレートエンジン(Jinja2)で HTML を生成
  • /predict エンドポイントで機械学習モデルをどんな言語からでも利用可能に
  • 内部で LM Studio を呼べば、「API を使う側と作る側」を同時に体験できる
  • リクエスト内容の検証とエラーレスポンス(400)を適切に返す

Streamlit

  • Python スクリプトを書くだけで Web アプリが作れる
  • st.text_input() 関数, st.button() 関数, st.write() 関数などの UI 部品
  • @st.cache_resource で分類器などのリソースをキャッシュ
  • st.session_state 属性で再実行をまたいでデータを保持(会話履歴・判定履歴)
  • LM Studio サーバをバックエンドにすれば、重いモデルをアプリ側に持たなくて良い

チャットアプリ

  • 第 13 章の補足と同じサーバのチャットモデル(qwen/qwen3.5-9b)を /v1/chat/completions で呼び出す
  • messages に system / user / assistant の役割でやり取りを入れるのが LLM API の共通パターン
  • モデルは会話を覚えていないので、履歴全体を毎回送り直す

デプロイ

  • アプリをインターネット上に公開
  • Streamlit Community Cloud:Streamlit アプリを簡単に公開
  • Render, Google Cloud Run など様々な選択肢
  • ローカル LLM サーバ前提のアプリは、サーバへの到達性を確保する設計が必要

実装のポイント

  • 分類器を pickle で保存・読み込み(Embedding モデルはサーバ側に常駐)
  • キャッシュを活用して高速化
  • エラーハンドリングを適切に行う
  • 環境変数で秘密情報・サーバ URL を管理

Streamlit と Flask の使い分け

  • デモやプロトタイプを素早く作る → Streamlit(A.4)
  • 他のプログラムから呼び出す API・画面を自由に設計したいアプリ → Flask(A.3)

おわりに

本書では、全 14 章と付録を通じて、Python の基礎から機械学習の基本、画像・テキストへの応用、深層学習と生成 AI の活用、そして機械学習アプリの作り方までを学んできました。

学んできたこと

章 テーマ 内容
1 イントロダクション 機械学習の全体像、実行環境の準備
2 Python入門 Python の基本文法
3 データ可視化 Matplotlib・Seaborn によるデータの可視化
4 機械学習の基礎 機械学習の種類、学習と予測の流れ、scikit-learn の基本
5 前処理と特徴量選択 欠損値処理、スケーリング、エンコーディング、特徴量選択、パイプライン
6 回帰 単回帰・重回帰・多項式回帰、正則化、回帰の評価指標
7 分類 ロジスティック回帰、ナイーブベイズ、k-近傍法、SVM、分類の評価指標
8 木構造とアンサンブル学習 決定木、ランダムフォレスト、勾配ブースティング、特徴量の重要度
9 教師なし学習 k-means 法、階層的クラスタリング、次元削減
10 画像処理の基礎 画像データの基礎、しきい値処理、フィルタリング、エッジ検出、HOG 特徴量
11 画像データを用いた機械学習 画像分類、画像認識、CNN の紹介
12 自然言語処理の基礎 形態素解析、テキストの前処理、BoW・TF-IDF、類似文書検索
13 テキストデータを用いた機械学習 文書分類、感情分析、Word2Vec・Doc2Vec、Transformer、Sentence Transformers
14 深層学習の基礎と活用 ニューラルネットワークの基礎と実践、CNN、Transformer、生成 AI の活用
付録 機械学習アプリの作り方 モデルの保存、推論処理、Flask・Streamlit、デプロイ

機械学習プロジェクトの流れ

本書で学んだ内容を組み合わせると、機械学習プロジェクトの一連の流れを自分の手で実践できます。

graph TB
    A("1. 問題の定義") --> B("2. データの収集・整理")
    B --> C("3. データの可視化・探索(第 3 章)")
    C --> D("4. 特徴量の抽出・前処理<br/>(第 5 章/画像:第 10・11 章/テキスト:第 12・13 章)")
    D --> E("5. モデルの選択・学習<br/>(第 4〜9 章、第 11・13・14 章)")
    E --> F("6. モデルの評価・改善<br/>(汎化性能・評価指標:第 5〜7 章)")
    F -->|改善を繰り返す| D
    F --> G("7. デプロイ・公開(付録)")

    classDef proc fill:#D9F0EA,stroke:#009E73,stroke-width:2px,color:#000
    classDef out fill:#F5ECE2,stroke:#BF8040,stroke-width:2px,color:#000
    class A,B,D,E,G proc
    class C,F out

今後の学習に向けて

本書で学んだ内容は、機械学習を実際に使うための土台です。さらに進むなら、次のようなトピックがあります。

  • 深層学習の実装: PyTorch などのフレームワークで CNN や Transformer を自分で組み、学習の中身まで追う
  • 大規模言語モデル(LLM)の活用: RAG(検索と生成の組み合わせ)、エージェント、ファインチューニング
  • 強化学習: ゲーム AI、ロボット制御
  • MLOps: モデルの運用・監視・再学習の仕組み

機械学習は日々進化している分野です。本書で学んだ基礎を土台に、興味のあるテーマから学習を続けてください。