#準備(すべてに共通) # PandasとNumpyをインポート import pandas as pd import numpy as np # 日本語化Matplotlibもインポート import matplotlib.pyplot as plt #↓の1行は提出時にはコメントアウトしてください !pip install japanize-matplotlib import japanize_matplotlib # Seabornもインポート import seaborn as sns # pickleをインポート(モデルの保存用) import pickle #データフレームをきれいに表示するメソッド from IPython.display import display #標準化はよく使うのでインポート from sklearn.preprocessing import StandardScaler
#データの取り込み
data = pd.read_csv("https://www2.cmds.kobe-u.ac.jp/~masa-n/dshandson/exam-pca.csv", index_col="受験番号")
display(data) df = data.copy() #要約統計量 display(df.describe()) #ヴァイオリン・プロット sns.violinplot(df) plt.show() #相関係数 sns.heatmap(df.corr(), annot=True)
#5科目の合計を計算
total = pd.DataFrame(df.sum(axis=1), columns=["合計"])
display(total)
#横棒グラフに可視化する
total.sort_values("合計").plot.barh(title="5科目の合計", figsize=(8,16))
#積み上げ棒グラフで表示する(オプショナル)
#df.loc[total.sort_values("合計").index].plot.barh(stacked=True, figsize=(8,16))
#66番と19番の得点を表示 display(df.loc[[66,19],:]) display(total.loc[[66,19]])
新しい軸 = w1*国語 + w2*数学 + w3*英語 + w4*物理 + w5*化学
from sklearn.decomposition import PCA
pca = PCA(ハイパーパラメータ)
#標準化されたデータフレームで学習させる
pca.fit(df_sc)
#主成分のラベル (データフレームの列・行に名前を付ける際に使う)
labels = [f"第{i+1}主成分" for i in range(pca.n_components_)]
#主成分得点に変換.データフレームに入れなおす
df_pca = pd.DataFrame(pca.transform(df_sc), index=df_sc, columns=labels)
#主成分得点を表示
print("【主成分得点】")
display(df_pca)
#主成分負荷量
df_comp = pd.DataFrame(pca.components_, index=labels, columns=df_sc.columns)
#主成分負荷量を表示
print("【主成分負荷量】")
display(df_comp)
#分散,寄与率
df_var = pd.DataFrame(pca.explained_variance_, index=labels, columns=["分散"])
df_var["寄与率"] = pca.explained_variance_ratio_
df_var["累積寄与率"] = pca.explained_variance_ratio_.cumsum()
print("【分散・寄与率】")
display(df_var)
#まずは標準化 sc = StandardScaler() sc.fit(df) df_sc = pd.DataFrame(sc.transform(df), index=df.index, columns=df.columns)
#主成分分析を行う.次元数はデフォルトで元データの次元数(5)になる
from sklearn.decomposition import PCA
pca = PCA()
pca.fit(df_sc)
#主成分のラベル (データフレームの列・行に名前を付ける際に使う)
labels = [f"第{i+1}主成分" for i in range(pca.n_components_)]
#主成分得点に変換.データフレームに入れなおす
df_pca = pd.DataFrame(pca.transform(df_sc), index=df_sc.index, columns=labels)
#主成分得点を表示
print("【主成分得点】")
display(df_pca)
#主成分負荷量
df_comp = pd.DataFrame(pca.components_, index=labels, columns=df_sc.columns)
#主成分負荷量を表示
print("【主成分負荷量】")
display(df_comp)
#分散,寄与率
df_var = pd.DataFrame(pca.explained_variance_, index=labels, columns=["分散"])
df_var["寄与率"] = pca.explained_variance_ratio_
df_var["累積寄与率"] = pca.explained_variance_ratio_.cumsum()
print("【分散・寄与率】")
display(df_var)
#第1,第2主成分のみを取り出す → 次元を2に削減 df_dim = df_pca.iloc[:,[0,1]] display(df_dim) #散布図を描く ax = sns.scatterplot(df_dim, x="第1主成分", y="第2主成分") #データフレームの各行を取り出し,各ポイントに受験番号(インデクス)をつける for idx, row in df_dim.iterrows(): ax.text(row["第1主成分"], row["第2主成分"], idx) #細かい装飾 ax.grid() ax.axvline(x=0, c="red") ax.axhline(y=0, c="blue")
【売上データ】
【問題】
→ 特徴量エンジニアリングによって,新しい特徴量を作る
#準備(すべてに共通)
# PandasとNumpyをインポート
import pandas as pd
import numpy as np
# 日本語化Matplotlibもインポート
import matplotlib.pyplot as plt
#↓の1行は提出時にはコメントアウトしてください
!pip install japanize-matplotlib
import japanize_matplotlib
# Seabornもインポート
import seaborn as sns
# pickleをインポート(モデルの保存用)
import pickle
# pandasのデータフレームを表示する関数
from IPython.display import display
#データをロードする(エクセルデータの読み込み)
data = pd.read_excel("https://www2.cmds.kobe-u.ac.jp/~masa-n/dshandson/yogurt.xlsx")
data
#型チェック data.dtypes
#整形
df = data.copy()
#売上日付をインデクスに
df = df.set_index("売上日付")
df
#売上個数を可視化 #箱ひげ図 df["売上数"].plot.box()
#時系列 df["売上数"].plot()
#月別に可視化
for y in [2021, 2022]:
for i in range(1,13):
df[(df.index.year==y)&(df.index.month==i)]["売上数"].plot.bar(title=f"{y}年{i}月", figsize=(8,6))
plt.show()
ヨーグルトAの売上数に関係ありそうなものは何か?
df["曜日"] = df.index.day_of_week
df["日"] = df.index.day
df["月"] = df.index.month
df["単価"] = df["売上額"] / df["売上数"]
n=3
for i in range(1, n+1):
#df["列"].shift(i)で,列を下にi桁ずらすことができる
df[f"{i}日前売上数"] = df["売上数"].shift(i)
df
#天気のデータを拾ってくる.インデクスを日付に
data_weather = pd.read_csv("どこかの天気のデータ.csv")
df_w = data_weather.set_index("日付")
#売上データとマージする (参考:Python基礎演習6.2)
df_merged = pd.merge(df, df_w, left_index=True, right_index=True)
多項式を使って,特徴量を作成するライブラリ
from sklearn.preprocessing import PolynomialFeatures poly = PolynomialFeatures(ハイパーパラメータ) #データフレームをフィットさせる poly.fit(df) #dfは特徴量作成の元になる列を含んだデータフレーム #データフレームを変換して,データフレームに入れなおす df_poly = pd.DataFrame(poly.transform(df), index=df.index, columns=poly_features_names_out()) #確認する df_poly
#データフレームを適当に作る
df_sample = pd.DataFrame(data={"a":[1,2,3,4,5], "b":[60,70,80,90,100]})
df_sample
#多項式特徴量を作成する
from sklearn.preprocessing import PolynomialFeatures
poly=PolynomialFeatures(degree=3,include_bias=False)
poly.fit(df_sample)
df_poly = pd.DataFrame(poly.transform(df_sample), index=df_sample.index,
columns=poly.get_feature_names_out())
df_poly
# KFoldの処理で分割時の条件を指定 from sklearn.model_selection import KFold kf = KFold(n_splits = 3, shuffle = True, random_state = 0)
# cross_validate関数をインポートする from sklearn.model_selection import cross_validate
# 線形回帰モデルの選択 from sklearn import linear_model model = linear_model.LinearRegression() # 交差検証 result = cross_validate(model, X, y, cv = kf, scoring = 'r2', return_train_score = True) print(result)
#平均値を計算する sum(result['test_score']) / len(result['test_score'])
from sklearn.model_selection import StratfiedKFold skf = StratfiedKFold(n_splits = 3, shuffle = True, random_state = 0)
# train_test_split関数をインポートし、X, yのそれぞれを訓練データ、検証データ、テストデータに分けていく from sklearn.model_selection import train_test_split
# まず、訓練・検証データとテストデータに分ける (訓練・検証:テスト=8:2) X_train_val, X_test, y_train_val, y_test = train_test_split(X, y, test_size=0.2, random_state=1234)
# さらに、訓練データと検証データに分ける (訓練:検証=7:3) X_train, X_val, y_train, y_val = train_test_split(X_train_val, y_train_val, test_size=0.3, random_state=1234)
# GridSearchCVクラスのインポート from sklearn.model_selection import GridSearchCV
# 学習に使用するアルゴリズムの定義 estimator = tree.DecisionTreeRegressor(random_state=0)
# 探索するハイパーパラメータと範囲の定義
param_grid = [{
'max_depth': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
'min_samples_split': [2, 10, 20]
}]
# データセット分割数を定義 cv = 5
# GridSearchCVクラスを用いたモデルの定義
tuned_dt = GridSearchCV(estimator=estimator,
param_grid=param_grid,
cv=cv, return_train_score=False)
# モデルの学習・検証 tuned_dt.fit(X_train_val, y_train_val)
# 検証結果の確認 pd.DataFrame(tuned_dt.cv_results_).T
# 最も予測精度の高かったハイパーパラメータの確認 tuned_dt.best_params_
{'max_depth': 4, 'min_samples_split': 2}# 最も予測精度の高かったモデルの引き継ぎ best_dt = tuned_dt.best_estimator_
# モデルの検証
print('train_val score : ', best_dt.score(X_train_val, y_train_val))
print('test score : ', best_dt.score(X_test, y_test))
train_val score : 0.6089775598869998 test score : 0.5767345219363884
!pip install pulp # モジュールをインポート(同時にCBCソルバがインストールされる) from pulp import *
# 工場のリスト(中身は供給上限) capacity = [6, 10]
# 取引先のリスト(中身は需要量) demand = [4, 6, 3, 3]
# 単位費用の行列 c_ij
cost = {(0, 0): 3,
(0, 1): 7,
(0, 2): 11,
(0, 3): 8,
(1, 0): 6,
(1, 1): 7,
(1, 2): 8,
(1, 3): 9 }
#問題を生成する
model = LpProblem("Transportation", LpMinimize)
# 決定変数x_ijを生成する
x = {(i,j): LpVariable("x{}-{}".format(i,j), lowBound=0, upBound=demand[j]) for i,j in cost}
# 目的関数 model += lpSum([cost[i,j] * x[i,j] for i,j in cost]), "Objective"
# 制約条件
# 工場の出荷上限
for i, Ci in enumerate(capacity):
model += lpSum([x[i,j] for j in range(len(demand))]) <= Ci, "Capacity{}".format(i)
# 取引先の需要
for j, dj in enumerate(demand):
model += lpSum([x[i,j] for i in range(len(capacity))]) == dj, "demand{}".format(j)
# 求解、問題modelの最適解が,CBCソルバによって計算される model.solve()
# 結果の確認
print("Optimal Value =", value(model.objective))
for var in x.values():
# 誤差以上の値を持っている変数だけprint
if var.varValue > 1e-4:
print(var, var.varValue)
Optimal Value = 103.0 x0_0 4.0 x0_3 2.0 x1_1 6.0 x1_2 3.0 x1_3 1.0
本講義では,7.4 回まで機械学習モデルの構築・評価を扱った
モデルを実際に運用化するには,モデルのデプロイ (deploy)が必須
◇ ソフトウェアの観点からサーバの共通的な特徴をいえば
◇ なぜプログラムにアドレスを付けるのか?
◇サーバアドレスの一般的な形式
◇ サーバの一般的な種類
◇ Application Programming Interface (API)
◇ REST API (または RESTful API)
アヤメ分類問題を復習しましょう【参考】
◇ 説明変数
◇ 目的変数
◇ 準備1
※ 欠損値穴埋めや,特徴量の標準化をここで省略
# === モデルの構築・評価 ===
# PandasとNumpyをインポート
import pandas as pd
import numpy as np
# train_test_splitをインポート(データの分割用)
from sklearn.model_selection import train_test_split
#アヤメデータの取得
from sklearn.datasets import load_iris
iris = load_iris()
# 訓練データと検証データに分割
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.1)
# ロジスティック回帰による学習
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(random_state = 0, C = 0.1, multi_class = 'auto', solver = 'lbfgs')
model.fit(X_train, y_train)
# モデル評価
print("Model score: ", model.score(X_train, y_train))
print("Test Accuracy: ", model.score(X_test, y_test))
【実行結果】
Model score: 0.9481481481481482 Test Accuracy: 1.0
# モデルデプロイのためのモジュールをインストール ※追記:バージョン指定 ## 環境による事情ですが,もしかして''全てコードの実行前に,さきに必要な全てのモジュールをインストールする順序の守りが必要'' !pip install fastapi==0.104 nest-asyncio==1.5.8 pyngrok==7.0.1 uvicorn==0.24.0
【実行】
from fastapi import FastAPI # Python で RESTful API を構築するための最新の Web フレームワーク
from fastapi.middleware.cors import CORSMiddleware # CORSまたは「オリジン間リソース共有」
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=['*'],
allow_credentials=True,
allow_methods=['*'],
allow_headers=['*'],
)
@app.get('/')
async def root():
return {'hello': 'world'}
from flask import Flask, request, jsonify # Web アプリケーションフレームワークや受送信,データ整形に必要
from pydantic import BaseModel, conlist # リクエストbodyを定義するために必要
from typing import List # ネストされたBodyを定義するために必要
# リクエストbodyを定義
class Iris(BaseModel):
data: List[conlist(float, min_items=4, max_items=4)]
@app.post("/predict")
async def get_predictions(iris: Iris):
data = dict(iris)['data']
iris_types = {
0: 'setosa',
1: 'versicolor',
2: 'virginica'
}
prediction = list(map(lambda x: iris_types[x], model.predict(data).tolist()))
log_proba = model.predict_log_proba(data).tolist()
return {"prediction": prediction, "log_proba": log_proba}
import nest_asyncio # 非同期処理
from pyngrok import ngrok # ローカルサーバを外部ネットワークに簡単に公開できるトンネリングツール
import uvicorn # ASGI (Asynchronous Server Gateway Interface) Webサーバ
ngrok_tunnel = ngrok.connect(8000)
print('Public URL:', ngrok_tunnel.public_url)
nest_asyncio.apply()
uvicorn.run(app, port=8000)
【実行結果】
Public URL: https://f627-34-125-200-230.ngrok.io ← これがサーバのアドレス ※ 毎回実行時に変わる INFO: Started server process [287] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
◇ サーバ稼働状況の確認
◇ 準備2
# 送信用とJSON関係のモジュールをインポート
import requests
import json
# POST先URL (上記出力のサーバアドレス + APIパラメータ)
url = "https://f627-34-125-200-230.ngrok.io" + "/predict"
# JSON形式のデータ (アヤメ3組を入力してみる)
json_data = {
"data":[
[1, 2, 3, 4],
[3, 7, 2, 9],
[6, 8, 1, 3]
]
}
# POST送信
response = requests.post(
url,
data = json.dumps(json_data) # データをJSON形式にエンコード
)
# レスポンス内容の確認
res_data = response.text
print(res_data)
【実行結果】
{"prediction":["setosa","virginica","setosa"],"log_proba":[[-0.9210444567587419,-0.9267194253195576,-1.579652054455776],[-2.205814584875692,-4.76721288044762,-0.12631725142856434],[-0.011910465650825333,-4.763788201065021,-5.711836534624314]]}
◇ サーバの停止 (shutdown)
【実行結果】
INFO: Shutting down INFO: Waiting for application shutdown. INFO: Application shutdown complete. INFO: Finished server process [287]
◇ その他