見出し画像

StanとRでベイズ統計モデリングをPyMC Ver.5で写経~第5章「5.2 二項ロジスティック回帰」

第5章「基本的な回帰とモデルのチェック」

書籍の著者 松浦健太郎 先生


この記事は、テキスト第5章「基本的な回帰とモデルのチェック」の5.2節「二項ロジスティック回帰」の PyMC5写経 を取り扱います。
尤度関数に二項分布を用います。
二項分布のパラメータ、成功確率$${p}$$はロジスティック関数を介して線形モデルが適用されます。
前節で拵えた描画系コードを使いまわします!

はじめに


StanとRでベイズ統計モデリングの紹介

この記事は書籍「StanとRでベイズ統計モデリング」(共立出版、「テキスト」と呼びます)のベイズモデルを用いて、PyMC Ver.5で「実験的」に写経する翻訳的ドキュメンタリーです。

テキストは、2016年10月に発売され、ベイズモデリングのモデル式とプログラミングに関する丁寧な解説とモデリングの改善ポイントを網羅するチュートリアル「実践解説書」です。もちろん素晴らしいです!
アヒル本」の愛称で多くのベイジアンに愛されてきた書籍です!

テキストに従ってStanとRで実践する予定でしたが、RのStan環境を整えることができませんでした(泣)
そこでこのシリーズは、テキストのベイズモデルをPyMC Ver.5に書き換えて実践します。

引用表記

この記事は、出典に記載の書籍に掲載された文章及びコードを引用し、適宜、掲載文章とコードを改変して書いています。
【出典】
「StanとRでベイズ統計モデリング」初版第13刷、著者 松浦健太郎、共立出版

記事中のイラストは、「かわいいフリー素材集いらすとや」さんのイラストをお借りしています。
ありがとうございます!

PyMC環境の準備

Anacondaを用いる環境構築とGoogle ColaboratoryでPyMCを動かす方法について、次の記事にまとめています。
「PyMCを動かすまでの準備」章をご覧ください。


5.2 二項ロジスティック回帰


インポート

### インポート

# 数値・確率計算
import pandas as pd
import numpy as np
import scipy.stats as stats

# PyMC
import pymc as pm
import arviz as az

# 描画
import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams['font.family'] = 'Meiryo'

# ワーニング表示の抑制
import warnings
warnings.simplefilter('ignore')

サンプルコードのデータを読み込みます。

### データの読み込み ◆データファイル5.2 data-attendance-2.txtの構成
# PersonID:学生ID、A:バイト好き区分(1:好き), Score:学問の興味の強さ(0~200)
# M:総授業回数(3か月間), Y:授業出席回数(3か月間)

data = pd.read_csv('./data/data-attendance-2.txt')
print('data.shape: ', data.shape)
display(data.head())

【実行結果】

データの前処理を行います。
「出席回数$${Y}$$ / 総授業回数$${M}$$」で出席率$${ratio}$$を求めます。

### データの前処理 出席率ratio列の追加

data['ratio'] = data['Y'] / data['M']
print('data.shape: ', data.shape)
display(data.head())

【実行結果】

5.2.2 データの分布の確認

散布図行列を描画します。図5.6相当です。
なるべくテキストに寄せるように努力しました。
axes1つごとにコードを書いたので絵巻物のようなコードが出来上がりました。

### 散布図行列の描画 ◆図5.6

## 描画領域の指定
fig, ax = plt.subplots(5, 5, figsize=(12, 12))
ax = ax.ravel() # 1次元でaxesを指定したいので

## 番地0,0:ヒストグラムの描画(棒グラフを使用)
bar_A = data.A.value_counts().sort_index()
sns.barplot(ax=ax[0], x=bar_A.index, y=bar_A, hue=bar_A.index, palette='tab10',
            alpha=0.5, ec='white', legend=None)
ax[0].set(ylabel='A', xlabel=None)
ax[0].grid(lw=0.5)

## 番地0,1:スピアマンの順位相関係数の描画
ax[1].set_axis_off()
corr, pval = stats.spearmanr(data.A, data.Score)
ax[1].text(x=0.4, y=0.4, s=round(corr * 100), fontsize=30)

## 番地0,2:スピアマンの順位相関係数の描画
ax[2].set_axis_off()
corr, pval = stats.spearmanr(data.A, data.M)
ax[2].text(x=0.2, y=0.4, s=round(corr * 100), fontsize=30)

## 番地0,3:スピアマンの順位相関係数の描画
ax[3].set_axis_off()
corr, pval = stats.spearmanr(data.A, data.Y)
ax[3].text(x=0.3, y=0.4, s=round(corr * 100), fontsize=30)

## 番地0,4:スピアマンの順位相関係数の描画
ax[4].set_axis_off()
corr, pval = stats.spearmanr(data.A, data.ratio)
ax[4].text(x=0.2, y=0.4, s=round(corr * 100), fontsize=30)

## 番地1,0:箱ひげ図+スウォームプロットの描画
sns.boxplot(ax=ax[5], x=data.A, y=data.Score, hue=data.A, fill=False,
            legend=None)
sns.swarmplot(ax=ax[5], x=data.A, y=data.Score, hue=data.A, size=8, alpha=0.5,
              legend=None)
ax[5].set(xlabel=None)
ax[5].grid(lw=0.5)

## 番地1,1:ヒストグラムの描画
sns.histplot(ax=ax[6], data=data, x='Score', hue='A', bins=10, kde=True,
             ec='white', legend=None)
ax[6].set(xlabel=None, ylabel=None)
ax[6].grid(lw=0.5)

## 番地1,2:スピアマンの順位相関係数の描画
ax[7].set_axis_off()
corr, pval = stats.spearmanr(data.Score, data.M)
ax[7].text(x=0.3, y=0.4, s=round(corr * 100), fontsize=30)

## 番地1,3:スピアマンの順位相関係数の描画
ax[8].set_axis_off()
corr, pval = stats.spearmanr(data.Score, data.Y)
ax[8].text(x=0.3, y=0.4, s=round(corr * 100), fontsize=30)

## 番地1,4:スピアマンの順位相関係数の描画
ax[9].set_axis_off()
corr, pval = stats.spearmanr(data.Score, data.ratio)
ax[9].text(x=0.3, y=0.4, s=round(corr * 100), fontsize=30)

## 番地2,0:箱ひげ図+スウォームプロットの描画
sns.boxplot(ax=ax[10], x=data.A, y=data.M, hue=data.A, fill=False,
            legend=None)
sns.swarmplot(ax=ax[10], x=data.A, y=data.M, hue=data.A, size=8, alpha=0.5,
              legend=None)
ax[10].grid(lw=0.5)

## 番地2,1:散布図の描画
sns.scatterplot(ax=ax[11], data=data, x='Score', y='M', hue='A', size='A',
                style='A', markers=['o', '^'], sizes=(80, 80), alpha=0.5,
                legend=None)
ax[11].set(ylabel=None)
ax[11].grid(lw=0.5)

## 番地2,2:ヒストグラムの描画
sns.histplot(ax=ax[12], data=data, x='M', hue='A', bins=10, kde=True, ec='white',
             legend=None)
ax[12].set(ylabel=None)
ax[12].grid(lw=0.5)

## 番地2,3:スピアマンの順位相関係数の描画
ax[13].set_axis_off()
corr, pval = stats.spearmanr(data.M, data.Y)
ax[13].text(x=0.3, y=0.4, s=round(corr * 100), fontsize=30)

## 番地2,4:スピアマンの順位相関係数の描画
ax[14].set_axis_off()
corr, pval = stats.spearmanr(data.M, data.ratio)
ax[14].text(x=0.2, y=0.4, s=round(corr * 100), fontsize=30)

## 番地3,0:箱ひげ図+スウォームプロットの描画
sns.boxplot(ax=ax[15], x=data.A, y=data.Y, hue=data.A, fill=False,
            legend=None)
sns.swarmplot(ax=ax[15], x=data.A, y=data.Y, hue=data.A, size=8, alpha=0.5,
              legend=None)
ax[15].grid(lw=0.5)

## 番地3,1:散布図の描画
sns.scatterplot(ax=ax[16], data=data, x='Score', y='Y', hue='A', size='A',
                style='A', markers=['o', '^'], sizes=(80, 80), alpha=0.5,
                legend=None)
ax[16].set(ylabel=None)
ax[16].grid(lw=0.5)

## 番地3,2:散布図の描画
sns.scatterplot(ax=ax[17], data=data, x='M', y='Y', hue='A', size='A',
                style='A', markers=['o', '^'], sizes=(80, 80), alpha=0.5,
                legend=None)
ax[17].set(ylabel=None)
ax[17].grid(lw=0.5)

## 番地3,3:ヒストグラムの描画
sns.histplot(ax=ax[18], data=data, x='Y', hue='A', bins=10, kde=True, ec='white',
             legend=None)
ax[18].set(ylabel=None)
ax[18].grid(lw=0.5)

## 番地3,4:スピアマンの順位相関係数の描画
ax[19].set_axis_off()
corr, pval = stats.spearmanr(data.Y, data.ratio)
ax[19].text(x=0.4, y=0.4, s=round(corr * 100), fontsize=30)

## 番地4,0:箱ひげ図+スウォームプロットの描画
sns.boxplot(ax=ax[20], x=data.A, y=data.ratio, hue=data.A, fill=False,
            legend=None)
sns.swarmplot(ax=ax[20], x=data.A, y=data.ratio, hue=data.A, size=8, alpha=0.5,
              legend=None)
ax[20].grid(lw=0.5)

## 番地4,1:散布図の描画
sns.scatterplot(ax=ax[21], data=data, x='Score', y='ratio', hue='A', size='A',
                style='A', markers=['o', '^'], sizes=(80, 80), alpha=0.5,
                legend=None)
ax[21].set(ylabel=None)
ax[21].grid(lw=0.5)

## 番地4,2:散布図の描画
sns.scatterplot(ax=ax[22], data=data, x='M', y='ratio', hue='A', size='A',
                style='A', markers=['o', '^'], sizes=(80, 80), alpha=0.5,
                legend=None)
ax[22].set(ylabel=None)
ax[22].grid(lw=0.5)

## 番地4,3:散布図の描画
sns.scatterplot(ax=ax[23], data=data, x='Y', y='ratio', hue='A', size='A',
                style='A', markers=['o', '^'], sizes=(80, 80), alpha=0.5,
                legend=None)
ax[23].set(ylabel=None)
ax[23].grid(lw=0.5)

## 番地4,4:ヒストグラムの描画
sns.histplot(ax=ax[24], data=data, x='ratio', hue='A', bins=10, kde=True,
             ec='white', legend=None)
ax[24].set(ylabel=None)
ax[24].grid(lw=0.5)

plt.tight_layout();

【実行結果】

5.2.3 メカニズムの想像

テキスト図5.7のロジスティック関数を描画します。

### ロジスティック関数 ◆図5.7

## 設定
# ロジスティック関数の定義
def logistic(x):
    return 1 / (1 + np.exp(-x))
# x軸の値の設定
xvals = np.linspace(-6, 6, 1001)

## 描画
# 描画領域の設定
plt.figure(figsize=(6, 4))
ax = plt.subplot()
# ロジスティック関数の描画
ax.plot(xvals, logistic(xvals))
# x=0の垂直線の描画
ax.axvline(0, color='tab:red', lw=0.9, ls='--')
# y=0.5の垂直線の描画
ax.axhline(0.5, color='tab:red', lw=0.9, ls='--')
# 修飾
ax.set(xlabel='$x$', ylabel='$f\ (x)$', title='ロジスティック回帰',
       xticks=range(-6, 7, 3), yticks=np.linspace(0, 1, 5))
ax.grid(lw=0.5);

【実行結果】

5.2.5 Stanで実装

PyMC Ver.5 で実装します。
モデルの定義です。
なお、目的変数$${Y}$$の事後予測は MCMC で作成せず、PyMC の事後予測サンプリング「sample_posterior_predictive()」で作成します。
MCMC の NUTS サンプラーに numpyro を使いたいからです。

### モデルの定義 ◆model5-4.stan

with pm.Model() as model:
    
    ### データ関連定義
    ## coordの定義
    model.add_coord('data', values=data.index, mutable=True)
    ## dataの定義
    # 目的変数 Y
    Y = pm.ConstantData('Y', value=data['Y'].values, dims='data')
    # 説明変数 A
    A = pm.ConstantData('A', value=data['A'].values, dims='data')
    # 説明変数 Score / 200
    Score = pm.ConstantData('Score', value=data['Score'].values / 200,
                            dims='data')
    # 説明変数 M
    M = pm.ConstantData('M', value=data['M'].values, dims='data')

    ### 事前分布
    b1 = pm.Uniform('b1', lower=-10, upper=10)
    b2 = pm.Uniform('b2', lower=-10, upper=10)
    b3 = pm.Uniform('b3', lower=-10, upper=10)

    ### 線形予測子の逆ロジット変換
    q = pm.Deterministic('q', pm.invlogit(b1 + b2 * A + b3 * Score), dims='data')
    
    ### 尤度関数
    obs = pm.Binomial('obs', n=M, p=q, observed=Y, dims='data')

モデルの定義内容を見ます。

### モデルの表示
model

【実行結果】

### モデルの可視化
pm.model_to_graphviz(model)

【実行結果】

PythonでMCMCを実行します。

### 事後分布からのサンプリング 20秒 ◆run-model5-4.R
with model:
    idata = pm.sample(draws=1000, tune=1000, chains=4, target_accept=0.8,
                      nuts_sampler='numpyro', random_seed=1234)

【実行結果】

Pythonで事後分布からのサンプリングデータの確認を行います。
Rhatの確認から。
テキストの収束条件は「chainを3以上にして$${\hat{R}<1.1}$$のとき」です。

### r_hat>1.1の確認
# 設定
idata_in = idata         # idata名
threshold = 1.01         # しきい値

# しきい値を超えるR_hatの個数を表示
print((az.rhat(idata_in) > threshold).sum())

【実行結果】
収束条件を満たしています。

事後統計量を表示します。

### 推論データの要約統計情報の表示
var_names = ['b1', 'b2', 'b3', 'q']
pm.summary(idata, hdi_prob=0.95, var_names=var_names, round_to=3)

【実行結果】

トレースプロットを描画します。

### トレースプロットの表示
pm.plot_trace(idata, compact=True, var_names=var_names)
plt.tight_layout();

【実行結果】

$${Y}$$(モデル的には obs )の事後予測サンプリングを行います。

### Yの事後予測分布のサンプリング
with model:
    idata.extend(pm.sample_posterior_predictive(idata, random_seed=1234))

【実行結果】

ppcプロットを描画します。

### ppcプロットの描画
pm.plot_ppc(idata, num_pp_samples=100);

【実行結果】

5.2.6 推定結果の解釈

テキスト68ページ掲載のベイズ信用区間(50%, 95%)を含む事後統計量を表示します。

### パラメータの要約を確認 ◆テキスト68ページの要約統計量

## 統計量算出関数:mean,sd,2.5%,25%,50%,75%,97.5%点をデータフレーム化する
def make_stats_df(y):
    probs = [2.5, 25, 50, 75, 97.5]
    columns = ['mean', 'sd'] + [str(s) + '%' for s in probs]
    quantiles = pd.DataFrame(np.percentile(y, probs, axis=0).T, index=y.columns)
    tmp_df = pd.concat([y.mean(axis=0), y.std(axis=0), quantiles], axis=1)
    tmp_df.columns=columns
    return tmp_df

## 要約統計量の算出・表示
# 事後分布サンプリングデータから取り出すパラメータを設定
vars = ['b1', 'b2', 'b3']
# 事後分布サンプリングデータidataからパラメータを指定してデータフレーム化
param_samples = idata.posterior[vars].to_dataframe()
# 上記データフレームを統計量算出関数に与えて事後統計量データフレームを作成
params_stats_df = make_stats_df(param_samples)
# 事後統計量データフレームの表示
display(params_stats_df.round(2))

【実行結果】
ほぼテキストの事後統計量と同じです。

オッズを計算してみます。
アルバイト好き区分(arbeit )と学問興味スコア(score)に設定した値でオッズを計算します。

### オッズを求めてみる ◆テキスト68ページあたりの議論

# アルバイト好き区分(# 好き:1, そうでもない:0)と学問興味スコアを設定します
arbeit = 0      
score = 150

# オッズ計算関数の定義
def calc_odds(a, score, b1, b2, b3):
    return np.exp(b1 + b2 * a + b3 * score / 200)

# パラメータの事後分布サンプルの事後平均を取得
b1_mean = params_stats_df.loc['b1', 'mean']
b2_mean = params_stats_df.loc['b2', 'mean']
b3_mean = params_stats_df.loc['b3', 'mean']

# オッズを計算する
print(f'オッズ: {calc_odds(arbeit, score, b1_mean, b2_mean, b3_mean):.3f}')

【実行結果】

5.2.8 図によるモデルのチェック

■ 図5.2
図5.8のアルバイト好き区分別のベイズ予測区間を描画します。
出席率$${Y}$$の予測値は、事後予測サンプリングデータを用います。

### 観測値と予測値のプロット ◆図5.8

## 描画用データの作成 yPredの個人別の中央値と80%区間を算出
# 事後予測サンプリングデータからyPredを取り出し
y_pred_samples = (idata.posterior_predictive.obs
                  .stack(sample=('chain', 'draw')).data)
# サンプリングデータの10%,50%,90%パーセンタイル点を算出してデータフレーム化
y_pred_df = pd.DataFrame(
    np.quantile(y_pred_samples, q=[0.1, 0.5, 0.9], axis=1).T,
    columns=['10%', 'median', '90%'])
# 観測値dataと予測値y_pred_dfのデータフレームを結合
y_pred_df = pd.concat([data, y_pred_df], axis=1)
# 中央値と10%点の差、90%点と中央値の差を算出: errorbarで利用
y_pred_df['err_lower'] = y_pred_df['median'] - y_pred_df['10%'] 
y_pred_df['err_upper'] = y_pred_df['90%']  - y_pred_df['median']
# アルバイト0とアルバイト1に分離
y_pred_A0 = y_pred_df[y_pred_df['A']==0]
y_pred_A1 = y_pred_df[y_pred_df['A']==1]

## 描画処理
# 描画領域の指定
plt.figure(figsize=(6, 6))
ax = plt.subplot()
# アルバイト0の描画(エラーバー付き散布図)
ax.errorbar(y_pred_A0['Y'], y_pred_A0['median'],
            yerr=[y_pred_A0['err_lower'], y_pred_A0['err_upper']],
            color='tab:blue', alpha=0.7, marker='o', ms=10, linestyle='none',
            label='0')
# アルバイト1の描画(エラーバー付き散布図)
ax.errorbar(y_pred_A1['Y'], y_pred_A1['median'],
            yerr=[y_pred_A1['err_lower'], y_pred_A1['err_upper']],
            color='tab:orange', alpha=0.7, marker='^', ms=10, linestyle='none',
            label='1')
# 赤い対角線の描画
ax.plot([0, 80], [0, 80], color='red', ls='--')
# 修飾
ax.set(xlabel='Observed: $Y$の観測値', ylabel='Predicted: $Y$の予測値(中央値)',
       title='$Y$ の観測値と予測値(中央値)のプロット 80%区間バー付き')
ax.legend(title='アルバイト好き', bbox_to_anchor=(1, 1))
ax.grid(lw=0.5);

【実行結果】

MCMCサンプルの散布図行列を描画します。
(テキストでは描画なし)
seaborn の pairplot を利用しており、テキストの描画内容よりも簡略化しています。

### MCMCサンプルの散布図行列の描画

## 描画用データの作成
# MCMCサンプリングデータからq1, q50を取り出し
q1_samples = (idata.posterior['q'].to_dataframe().reset_index()
              .query('data==0').rename({'q': 'q1'}, axis=1))
q50_samples = (idata.posterior['q'].to_dataframe().reset_index()
               .query('data==49').rename({'q': 'q50'}, axis=1))
# 描画対象パラメータをデータフレーム化
plot_df = pd.concat([param_samples.reset_index(drop=True),
                     q1_samples.reset_index(drop=True)['q1'],
                     q50_samples.reset_index(drop=True)['q50']], axis=1)

## 描画処理
# 相関行列プロットの描画
g = sns.pairplot(plot_df, diag_kws={'kde': True, 'ec': 'white'})
# スピアマンの順位相関係数の表示のためのaxフラット化
ax = g.axes.ravel()

## スピアマンの順位相関係数の表示
# b1, b2
corr1, pval1 = stats.spearmanr(plot_df.b1, plot_df.b2)
ax[1].text(x=-0.43, y=0.7, s=round(corr1 * 100), fontsize=20, color='red')
# b1, b3
corr1, pval1 = stats.spearmanr(plot_df.b1, plot_df.b3)
ax[2].text(x=2.8, y=0.7, s=round(corr1 * 100), fontsize=20, color='red')
# b1, q1
corr1, pval1 = stats.spearmanr(plot_df.b1, plot_df.q1)
ax[3].text(x=0.74, y=0.25, s=round(corr1 * 100), fontsize=20, color='red')
# b1, q50
corr1, pval1 = stats.spearmanr(plot_df.b1, plot_df.q50)
ax[4].text(x=0.65, y=0.7, s=round(corr1 * 100), fontsize=20, color='red')
# b2, b3
corr1, pval1 = stats.spearmanr(plot_df.b2, plot_df.b3)
ax[7].text(x=3.1, y=-0.4, s=round(corr1 * 100), fontsize=20, color='red')
# b2, q1
corr1, pval1 = stats.spearmanr(plot_df.b2, plot_df.q1)
ax[8].text(x=0.73, y=-0.4, s=round(corr1 * 100), fontsize=20, color='red')
# b2, q50
corr1, pval1 = stats.spearmanr(plot_df.b2, plot_df.q50)
ax[9].text(x=0.65, y=-0.4, s=round(corr1 * 100), fontsize=20, color='red')
# b3, q1
corr1, pval1 = stats.spearmanr(plot_df.b3, plot_df.q1)
ax[13].text(x=0.73, y=3, s=round(corr1 * 100), fontsize=20, color='red')
# b3, q50
corr1, pval1 = stats.spearmanr(plot_df.b3, plot_df.q50)
ax[14].text(x=0.64, y=3, s=round(corr1 * 100), fontsize=20, color='red')
# q1, q50
corr1, pval1 = stats.spearmanr(plot_df.q1, plot_df.q50)
ax[19].text(x=0.65, y=0.73, s=round(corr1 * 100), fontsize=20, color='red');

【実行結果】

5.2 節は以上です。


シリーズの記事

次の記事

前の記事

目次

ブログの紹介


note で7つのシリーズ記事を書いています。
ぜひ覗いていってくださいね!

1.のんびり統計

統計検定2級の問題集を手がかりにして、確率・統計をざっくり掘り下げるブログです。
雑談感覚で大丈夫です。ぜひ覗いていってくださいね。
統計検定2級公式問題集CBT対応版に対応しています。
Python、EXCELのサンプルコードの配布もあります。

2.実験!たのしいベイズモデリング1&2をPyMC Ver.5で

書籍「たのしいベイズモデリング」・「たのしいベイズモデリング2」の心理学研究に用いられたベイズモデルを PyMC Ver.5で描いて分析します。
この書籍をはじめ、多くのベイズモデルは R言語+Stanで書かれています。
PyMCの可能性を探り出し、手軽にベイズモデリングを実践できるように努めます。
身近なテーマ、イメージしやすいテーマですので、ぜひぜひPyMCで動かして、一緒に楽しみましょう!

3.実験!岩波データサイエンス1のベイズモデリングをPyMC Ver.5で

書籍「実験!岩波データサイエンスvol.1」の4人のベイジアンによるベイズモデルを PyMC Ver.5で描いて分析します。
この書籍はベイズプログラミングのイロハをざっくりと学ぶことができる良書です。
楽しくPyMCモデルを動かして、ベイズと仲良しになれた気がします。
みなさんもぜひぜひPyMCで動かして、一緒に遊んで学びましょう!

4.楽しい写経 ベイズ・Python等

ベイズ、Python、その他の「書籍の写経活動」の成果をブログにします。
主にPythonへの翻訳に取り組んでいます。
写経に取り組むお仲間さんのサンプルコードになれば幸いです🍀

5.RとStanではじめる心理学のための時系列分析入門 を PythonとPyMC Ver.5 で

書籍「RとStanではじめる心理学のための時系列分析入門」の時系列分析をPythonとPyMC Ver.5 で実践します。
この書籍には時系列分析のテーマが盛りだくさん!
時系列分析の懐の深さを実感いたしました。
大好きなPythonで楽しく時系列分析を学びます。

6.データサイエンスっぽいことを綴る

統計、データ分析、AI、機械学習、Pythonのコラムを不定期に綴っています。
統計・データサイエンス書籍にまつわる記事が多いです。
「統計」「Python」「数学とPython」「R」のシリーズが生まれています。

7.Python機械学習プログラミング実践記

書籍「Python機械学習プログラミング PyTorch & scikit-learn編」を学んだときのさまざまな思いを記事にしました。
この書籍は、scikit-learnとPyTorchの教科書です。
よかったらぜひ、お試しくださいませ。

最後までお読みいただきまして、ありがとうございました。

この記事が参加している募集

#お金について考える

37,494件

この記事が気に入ったらサポートをしてみませんか?