ML Documentation

目次

機械学習の基礎知識と実践ガイド

1. 機械学習とは何か

1.1 定義と概念

機械学習(Machine Learning) とは、コンピュータに明示的なプログラミングなしで学習する能力を与える技術です。データからパターンを自動的に発見し、そのパターンを使って予測や判断を行います。

従来のプログラミング:ルール + データ → 答え
機械学習:データ + 答え → ルール

1.2 なぜ機械学習が必要か

  1. 複雑なパターンの発見: 人間が見つけにくい複雑なパターンを発見
  2. 自動化: 大量のデータを自動的に処理
  3. 適応性: 新しいデータに対して自動的に適応
  4. 予測能力: 未来の出来事を予測

1.3 機械学習の基本的な流れ

# 機械学習の基本的なワークフロー
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

# 1. データの準備
X, y = load_data()  # 特徴量とターゲット

# 2. データの分割
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# 3. 前処理
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 4. モデルの学習
model = LinearRegression()
model.fit(X_train_scaled, y_train)

# 5. 予測
predictions = model.predict(X_test_scaled)

# 6. 評価
mse = mean_squared_error(y_test, predictions)
print(f"Mean Squared Error: {mse}")

2. 機械学習の種類

2.1 教師あり学習(Supervised Learning)

定義: 入力データとそれに対応する正解ラベル(教師データ)を使って学習する手法

分類(Classification)

データを事前に定義されたカテゴリに分ける

# 二値分類の例:暗号通貨の価格が上昇するか下降するか
from sklearn.ensemble import RandomForestClassifier

# データの準備
features = ['rsi', 'macd', 'volume_ratio', 'price_momentum']
X = df[features]
y = df['price_direction']  # 0: 下降, 1: 上昇

# モデルの学習
classifier = RandomForestClassifier(n_estimators=100)
classifier.fit(X_train, y_train)

# 予測
predictions = classifier.predict(X_test)
probabilities = classifier.predict_proba(X_test)

print(f"予測: {predictions[0]}")
print(f"上昇確率: {probabilities[0][1]:.2%}")

分類の種類:
- 二値分類: 2つのクラスに分類(例:上昇/下降)
- 多クラス分類: 3つ以上のクラスに分類(例:大幅上昇/上昇/横ばい/下降/大幅下降)
- 多ラベル分類: 複数のラベルを同時に付与

回帰(Regression)

連続値を予測する

# 回帰の例:暗号通貨の将来価格を予測
from sklearn.linear_model import Ridge

# データの準備
X = df[['open', 'high', 'low', 'volume', 'rsi', 'macd']]
y = df['future_price']  # 5分後の価格

# モデルの学習
regressor = Ridge(alpha=1.0)
regressor.fit(X_train, y_train)

# 予測
predicted_price = regressor.predict(X_test)
print(f"予測価格: ${predicted_price[0]:.2f}")

回帰の種類:
- 線形回帰: 線形関係を仮定
- 多項式回帰: 非線形関係をモデル化
- リッジ回帰: 過学習を防ぐ正則化付き

2.2 教師なし学習(Unsupervised Learning)

定義: 正解ラベルなしでデータの構造やパターンを発見する手法

クラスタリング(Clustering)

似たデータをグループ化

# クラスタリングの例:取引パターンのグルーピング
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

# 特徴量の準備
features = df[['trade_volume', 'price_volatility', 'trade_frequency']]

# クラスタリング
kmeans = KMeans(n_clusters=4, random_state=42)
clusters = kmeans.fit_predict(features)

# 可視化
plt.scatter(features['trade_volume'], features['price_volatility'], 
           c=clusters, cmap='viridis')
plt.xlabel('取引量')
plt.ylabel('価格ボラティリティ')
plt.title('取引パターンのクラスタリング')

次元削減(Dimensionality Reduction)

高次元データを低次元に圧縮

# 次元削減の例:PCAによる特徴量圧縮
from sklearn.decomposition import PCA

# 100次元の特徴量を2次元に圧縮
pca = PCA(n_components=2)
features_2d = pca.fit_transform(high_dimensional_features)

# 説明分散比
print(f"説明分散比: {pca.explained_variance_ratio_}")
print(f"累積説明分散比: {sum(pca.explained_variance_ratio_):.2%}")

2.3 強化学習(Reinforcement Learning)

定義: エージェントが環境と相互作用しながら、報酬を最大化する行動を学習

# 強化学習の概念的な例:取引戦略の学習
class TradingEnvironment:
    def __init__(self):
        self.position = 0  # 現在のポジション
        self.balance = 10000  # 初期資金

    def step(self, action):
        # action: 0=待機, 1=買い, 2=売り
        reward = self.calculate_reward(action)
        new_state = self.get_state()
        done = self.is_episode_finished()
        return new_state, reward, done

    def calculate_reward(self, action):
        # 利益/損失に基づく報酬を計算
        pass

2.4 半教師あり学習(Semi-Supervised Learning)

少量のラベル付きデータと大量のラベルなしデータを組み合わせて学習

from sklearn.semi_supervised import LabelPropagation

# ラベル付きデータとラベルなしデータの準備
# -1 はラベルなしを示す
y_mixed = np.copy(y_true)
y_mixed[unlabeled_indices] = -1

# 半教師あり学習
model = LabelPropagation()
model.fit(X, y_mixed)

# ラベルなしデータの予測
predicted_labels = model.transduction_[unlabeled_indices]

3. 主要な機械学習アルゴリズム

3.1 線形モデル

線形回帰(Linear Regression)

最もシンプルな回帰モデル

from sklearn.linear_model import LinearRegression

# y = β₀ + β₁x₁ + β₂x₂ + ... + βₙxₙ
model = LinearRegression()
model.fit(X_train, y_train)

# 係数の確認
print(f"切片: {model.intercept_}")
print(f"係数: {model.coef_}")

ロジスティック回帰(Logistic Regression)

分類問題に使用される線形モデル

from sklearn.linear_model import LogisticRegression

# シグモイド関数を使って確率を出力
model = LogisticRegression()
model.fit(X_train, y_train)

# 確率の予測
probabilities = model.predict_proba(X_test)

3.2 決定木ベースのモデル

決定木(Decision Tree)

if-thenルールの木構造

from sklearn.tree import DecisionTreeClassifier
from sklearn.tree import export_text

# 決定木の学習
tree = DecisionTreeClassifier(max_depth=3)
tree.fit(X_train, y_train)

# 決定木の可視化
tree_rules = export_text(tree, feature_names=feature_names)
print(tree_rules)

ランダムフォレスト(Random Forest)

複数の決定木のアンサンブル

from sklearn.ensemble import RandomForestRegressor

# 100本の決定木を使用
rf = RandomForestRegressor(n_estimators=100, random_state=42)
rf.fit(X_train, y_train)

# 特徴量の重要度
feature_importance = pd.DataFrame({
    'feature': feature_names,
    'importance': rf.feature_importances_
}).sort_values('importance', ascending=False)

勾配ブースティング(Gradient Boosting)

決定木を逐次的に学習

from sklearn.ensemble import GradientBoostingRegressor
import xgboost as xgb

# scikit-learnの実装
gb = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1)
gb.fit(X_train, y_train)

# XGBoostの実装(より高速)
xgb_model = xgb.XGBRegressor(n_estimators=100, learning_rate=0.1)
xgb_model.fit(X_train, y_train)

3.3 ニューラルネットワーク

多層パーセプトロン(MLP)

基本的なニューラルネットワーク

from sklearn.neural_network import MLPRegressor

# 隠れ層が2層(100ユニット、50ユニット)のネットワーク
mlp = MLPRegressor(
    hidden_layer_sizes=(100, 50),
    activation='relu',
    max_iter=1000
)
mlp.fit(X_train, y_train)

深層学習(Deep Learning)

PyTorchを使った実装例

import torch
import torch.nn as nn

class SimpleNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleNN, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        return x

# モデルのインスタンス化
model = SimpleNN(input_size=10, hidden_size=50, output_size=1)

3.4 サポートベクターマシン(SVM)

マージン最大化による分類・回帰

from sklearn.svm import SVC, SVR

# 分類用SVM
svm_classifier = SVC(kernel='rbf', C=1.0, gamma='scale')
svm_classifier.fit(X_train, y_train)

# 回帰用SVM
svm_regressor = SVR(kernel='rbf', C=1.0, epsilon=0.1)
svm_regressor.fit(X_train, y_train)

3.5 k近傍法(k-NN)

最も近いk個のデータポイントから予測

from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor

# 分類
knn_classifier = KNeighborsClassifier(n_neighbors=5)
knn_classifier.fit(X_train, y_train)

# 回帰
knn_regressor = KNeighborsRegressor(n_neighbors=5)
knn_regressor.fit(X_train, y_train)

4. 機械学習の評価指標

4.1 分類の評価指標

from sklearn.metrics import (
    accuracy_score, precision_score, recall_score, f1_score,
    confusion_matrix, classification_report, roc_auc_score
)

# 基本的な指標
accuracy = accuracy_score(y_true, y_pred)
precision = precision_score(y_true, y_pred)
recall = recall_score(y_true, y_pred)
f1 = f1_score(y_true, y_pred)

print(f"正解率: {accuracy:.3f}")
print(f"適合率: {precision:.3f}")
print(f"再現率: {recall:.3f}")
print(f"F1スコア: {f1:.3f}")

# 混同行列
cm = confusion_matrix(y_true, y_pred)
print("混同行列:")
print(cm)

# 詳細レポート
print(classification_report(y_true, y_pred))

# ROC-AUC(確率出力がある場合)
if hasattr(model, 'predict_proba'):
    y_proba = model.predict_proba(X_test)[:, 1]
    auc = roc_auc_score(y_true, y_proba)
    print(f"ROC-AUC: {auc:.3f}")

評価指標の解説

4.2 回帰の評価指標

from sklearn.metrics import (
    mean_squared_error, mean_absolute_error, r2_score,
    mean_absolute_percentage_error
)

# 各種評価指標
mse = mean_squared_error(y_true, y_pred)
rmse = np.sqrt(mse)
mae = mean_absolute_error(y_true, y_pred)
r2 = r2_score(y_true, y_pred)
mape = mean_absolute_percentage_error(y_true, y_pred)

print(f"平均二乗誤差 (MSE): {mse:.3f}")
print(f"平方根平均二乗誤差 (RMSE): {rmse:.3f}")
print(f"平均絶対誤差 (MAE): {mae:.3f}")
print(f"決定係数 (R²): {r2:.3f}")
print(f"平均絶対パーセント誤差 (MAPE): {mape:.3%}")

5. 過学習と正則化

5.1 過学習(Overfitting)とは

訓練データに過度に適合し、新しいデータに対して性能が悪化する現象

import matplotlib.pyplot as plt

# 学習曲線の描画
def plot_learning_curves(model, X_train, y_train, X_val, y_val):
    train_errors = []
    val_errors = []

    for m in range(1, len(X_train), 100):
        model.fit(X_train[:m], y_train[:m])
        train_pred = model.predict(X_train[:m])
        val_pred = model.predict(X_val)

        train_errors.append(mean_squared_error(y_train[:m], train_pred))
        val_errors.append(mean_squared_error(y_val, val_pred))

    plt.plot(train_errors, label='訓練誤差')
    plt.plot(val_errors, label='検証誤差')
    plt.xlabel('訓練サンプル数')
    plt.ylabel('誤差')
    plt.legend()
    plt.title('学習曲線')

5.2 正則化手法

L1正則化(Lasso)

from sklearn.linear_model import Lasso

# L1正則化により、重要でない特徴量の係数を0にする
lasso = Lasso(alpha=0.1)
lasso.fit(X_train, y_train)

# 係数が0になった特徴量を確認
zero_coef = np.sum(lasso.coef_ == 0)
print(f"係数が0の特徴量数: {zero_coef}")

L2正則化(Ridge)

from sklearn.linear_model import Ridge

# L2正則化により、係数の大きさを制限
ridge = Ridge(alpha=1.0)
ridge.fit(X_train, y_train)

ドロップアウト(Neural Networks)

import torch.nn as nn

class RegularizedNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(100, 50)
        self.dropout = nn.Dropout(0.5)  # 50%のユニットをランダムに無効化
        self.fc2 = nn.Linear(50, 1)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.fc2(x)
        return x

5.3 交差検証(Cross-Validation)

from sklearn.model_selection import cross_val_score, KFold

# k分割交差検証
kfold = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=kfold, scoring='neg_mean_squared_error')

print(f"各分割のスコア: {-scores}")
print(f"平均スコア: {-scores.mean():.3f} (+/- {scores.std() * 2:.3f})")

6. 特徴量エンジニアリング

6.1 特徴量の作成

# 暗号通貨データの特徴量エンジニアリング例
def create_features(df):
    # 価格関連の特徴量
    df['returns'] = df['close'].pct_change()
    df['log_returns'] = np.log(df['close'] / df['close'].shift(1))
    df['price_range'] = df['high'] - df['low']
    df['price_position'] = (df['close'] - df['low']) / (df['high'] - df['low'])

    # 移動平均
    for window in [5, 10, 20, 50]:
        df[f'ma_{window}'] = df['close'].rolling(window=window).mean()
        df[f'ma_ratio_{window}'] = df['close'] / df[f'ma_{window}']

    # ボラティリティ
    df['volatility'] = df['returns'].rolling(window=20).std()

    # 出来高関連
    df['volume_ratio'] = df['volume'] / df['volume'].rolling(window=20).mean()

    return df

6.2 特徴量の選択

from sklearn.feature_selection import SelectKBest, f_regression, RFE

# 統計的手法による選択
selector = SelectKBest(score_func=f_regression, k=10)
X_selected = selector.fit_transform(X, y)

# 再帰的特徴量削減
rfe = RFE(estimator=LinearRegression(), n_features_to_select=10)
X_rfe = rfe.fit_transform(X, y)

# 選択された特徴量
selected_features = X.columns[rfe.support_]
print(f"選択された特徴量: {selected_features.tolist()}")

6.3 特徴量の変換

from sklearn.preprocessing import (
    StandardScaler, MinMaxScaler, RobustScaler,
    PolynomialFeatures, PowerTransformer
)

# 標準化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 正規化(0-1の範囲)
minmax = MinMaxScaler()
X_normalized = minmax.fit_transform(X)

# 外れ値に強いスケーリング
robust = RobustScaler()
X_robust = robust.fit_transform(X)

# 多項式特徴量
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(X)

# Box-Cox変換(正規分布に近づける)
power = PowerTransformer(method='box-cox')
X_transformed = power.fit_transform(X + 1)  # Box-Coxは正の値が必要

7. 実践的なTips

7.1 データの前処理チェックリスト

def preprocess_data(df):
    # 1. 欠損値の処理
    print(f"欠損値の数: {df.isnull().sum().sum()}")
    df = df.fillna(method='ffill')  # または適切な補完方法

    # 2. 外れ値の検出と処理
    Q1 = df.quantile(0.25)
    Q3 = df.quantile(0.75)
    IQR = Q3 - Q1
    outliers = ((df < (Q1 - 1.5 * IQR)) | (df > (Q3 + 1.5 * IQR))).sum()
    print(f"外れ値の数: {outliers.sum()}")

    # 3. データ型の確認
    print(df.dtypes)

    # 4. 重複データの除去
    duplicates = df.duplicated().sum()
    print(f"重複行数: {duplicates}")
    df = df.drop_duplicates()

    return df

7.2 モデル選択のガイドライン

def select_model(data_size, problem_type, interpretability_required):
    """
    データとタスクに応じたモデル選択
    """
    if problem_type == 'classification':
        if data_size < 1000:
            if interpretability_required:
                return "決定木 or ロジスティック回帰"
            else:
                return "SVM or k-NN"
        elif data_size < 10000:
            return "ランダムフォレスト or XGBoost"
        else:
            return "深層学習 or XGBoost"

    elif problem_type == 'regression':
        if data_size < 1000:
            if interpretability_required:
                return "線形回帰 or 決定木"
            else:
                return "SVR or ガウス過程"
        elif data_size < 10000:
            return "ランダムフォレスト or 勾配ブースティング"
        else:
            return "深層学習 or LightGBM"

7.3 ハイパーパラメータチューニング

from sklearn.model_selection import GridSearchCV, RandomizedSearchCV

# グリッドサーチ
param_grid = {
    'n_estimators': [100, 200, 300],
    'max_depth': [3, 5, 7, None],
    'min_samples_split': [2, 5, 10]
}

grid_search = GridSearchCV(
    RandomForestRegressor(),
    param_grid,
    cv=5,
    scoring='neg_mean_squared_error',
    n_jobs=-1
)
grid_search.fit(X_train, y_train)

print(f"最適パラメータ: {grid_search.best_params_}")
print(f"最高スコア: {-grid_search.best_score_:.3f}")

# ランダムサーチ(大規模な探索空間の場合)
from scipy.stats import randint, uniform

param_dist = {
    'n_estimators': randint(100, 500),
    'max_depth': randint(3, 20),
    'min_samples_split': randint(2, 20),
    'learning_rate': uniform(0.01, 0.3)
}

random_search = RandomizedSearchCV(
    GradientBoostingRegressor(),
    param_dist,
    n_iter=100,
    cv=5,
    scoring='neg_mean_squared_error',
    n_jobs=-1,
    random_state=42
)

8. 暗号通貨取引への応用例

8.1 価格予測モデルの構築

class CryptoPricePredictor:
    def __init__(self):
        self.feature_engineer = FeatureEngineering()
        self.scaler = RobustScaler()
        self.model = None

    def prepare_features(self, df):
        """特徴量の準備"""
        # テクニカル指標
        df['rsi'] = self.calculate_rsi(df['close'])
        df['macd'], df['signal'], _ = self.calculate_macd(df['close'])

        # 価格パターン
        df['higher_high'] = (df['high'] > df['high'].shift(1)).astype(int)
        df['lower_low'] = (df['low'] < df['low'].shift(1)).astype(int)

        # マーケット特徴
        df['spread'] = df['ask'] - df['bid']
        df['mid_price'] = (df['ask'] + df['bid']) / 2

        return df

    def train(self, X_train, y_train):
        """モデルの学習"""
        # 特徴量のスケーリング
        X_scaled = self.scaler.fit_transform(X_train)

        # アンサンブルモデル
        self.model = VotingRegressor([
            ('rf', RandomForestRegressor(n_estimators=100)),
            ('gb', GradientBoostingRegressor(n_estimators=100)),
            ('xgb', XGBRegressor(n_estimators=100))
        ])

        self.model.fit(X_scaled, y_train)

    def predict(self, X):
        """価格予測"""
        X_scaled = self.scaler.transform(X)
        prediction = self.model.predict(X_scaled)

        # 予測の信頼区間も計算
        predictions = []
        for estimator in self.model.estimators_:
            predictions.append(estimator.predict(X_scaled))

        predictions = np.array(predictions)
        mean_pred = predictions.mean(axis=0)
        std_pred = predictions.std(axis=0)

        return {
            'prediction': mean_pred,
            'lower_bound': mean_pred - 2 * std_pred,
            'upper_bound': mean_pred + 2 * std_pred
        }

8.2 異常検知システム

from sklearn.ensemble import IsolationForest
from sklearn.covariance import EllipticEnvelope

class AnomalyDetector:
    def __init__(self):
        self.models = {
            'isolation': IsolationForest(contamination=0.1),
            'elliptic': EllipticEnvelope(contamination=0.1)
        }

    def fit(self, X_normal):
        """正常データで学習"""
        for model in self.models.values():
            model.fit(X_normal)

    def detect(self, X):
        """異常検知"""
        results = {}
        for name, model in self.models.items():
            # -1: 異常, 1: 正常
            predictions = model.predict(X)
            scores = model.score_samples(X)
            results[name] = {
                'predictions': predictions,
                'anomaly_scores': scores
            }

        # アンサンブル結果
        ensemble_pred = np.mean([r['predictions'] for r in results.values()], axis=0)
        results['ensemble'] = {
            'predictions': (ensemble_pred < 0).astype(int),
            'confidence': np.abs(ensemble_pred)
        }

        return results

9. まとめとベストプラクティス

9.1 機械学習プロジェクトのチェックリスト

  1. 問題定義
    - [ ] 解決したい問題は明確か
    - [ ] 成功の評価基準は定義されているか
    - [ ] 必要なデータは入手可能か

  2. データ準備
    - [ ] データの品質は確認したか
    - [ ] 訓練/検証/テストデータに分割したか
    - [ ] データリークはないか

  3. モデル開発
    - [ ] ベースラインモデルは作成したか
    - [ ] 複数のアルゴリズムを試したか
    - [ ] ハイパーパラメータは調整したか

  4. 評価
    - [ ] 適切な評価指標を使用しているか
    - [ ] 過学習していないか
    - [ ] ビジネス要件を満たしているか

  5. デプロイメント
    - [ ] モデルの再現性は確保されているか
    - [ ] 監視体制は整っているか
    - [ ] 更新プロセスは定義されているか

9.2 よくある落とし穴と対策

  1. データリーク: 未来の情報が過去の予測に使われる
    ```python
    # 悪い例
    df['future_price'] = df['close'].shift(-1) # 未来の価格
    features = df[['close', 'future_price']] # リーク!

# 良い例
df['future_price'] = df['close'].shift(-1)
features = df[['close', 'volume', 'rsi']] # 現在の情報のみ
```

  1. 不均衡データ: クラスの偏りを考慮しない
    ```python
    from imblearn.over_sampling import SMOTE

# クラスの均衡化
smote = SMOTE(random_state=42)
X_balanced, y_balanced = smote.fit_resample(X_train, y_train)
```

  1. 過度な最適化: テストデータに対する過学習
    python # テストデータは最終評価のみに使用 # 開発中は訓練・検証データのみを使用

機械学習は強力なツールですが、適切に使用することが重要です。常に問題の本質を理解し、データの特性を把握し、モデルの限界を認識することが成功への鍵となります。