GitHubコードから読み解く!LinearRegression@scikit-learnのsample_weightオプション

Taketo Kimura
MICIN Developers
Published in
35 min readJan 9, 2019

はじめに

昨今の人工知能ブームのおかげで、気軽に機械学習を試せるライブラリがどんどん生み出されています。
Pythonのscikit-learnライブラリは、その最たる例でしょう。
(ネコも杓子もscikit-learn!私も大変お世話になっております🙇)

ライブラリを使えば、機械学習にそう通じていなくとも、ガシガシと適用できます。
先駆者のおかげで、かつてキャッチアップに要した苦労が、相当に解消されています。
感謝ですね。

しかし一方で、それは盲目的な分析者を生んでいるかと思います。
機械学習を何者か知らず、ただサンプル通りに動かしてみるだけだったり、無作為にパラメータチューニングをするだけだったり…。
それは、分析実施者にとってもライブラリ提供者にとっても、Unhappyな事態かと思います。
お互いを高め合うことがOSSの意義です。

では、具体的にどう向き合うのが良いのでしょうか?
私の推奨は、人が書いたソースコードを読むことです。
ソースコードは曖昧性の少ない世界共通言語です。
そこからのキャッチアップは、解釈がブレずに済むので、効率が良いものです。

そんな訳で、この記事では、OSSコードを読んでみて、アルゴリズムのキャッチアップするエクササイズに挑戦したいと思います。
対象は、scikit-learnのGitHubコードにおける、「sample_weight」というオプションとします。
このオプションは、LinearRegressionやRidgeの学習において使用可能なものであり、最近流行しているLIMEアルゴリズムの根幹部分を担うものでもあります。
(※LIMEについては別の記事にて、掘り下げようと思います。)

LinearRegressionについて

先ず、知らない人のためにLinearRegressionに触れます。

LinearRegressionは、観測点におけるX値とy値を紡ぐ法則性wを、以下の仮定を置いて解く方法です。
この解法を「最小二乗法」と言います。
尚、仮定における、XはN行D列の行列、yはN行1列のベクトル、wは1行D列のベクトルという想定です。

【最小二乗法の仮定】
X値とy値は線形の関係にあり、「y = wᵀX」で近似値が表現できる。
② 「y = wᵀX」で算出される近似値と、観測点のy値との間に発生する誤差は、正規分布に基づいている。

この仮定によって、解析的に一意のwを求めることができます。
Random性は無く、何度試しても同じwが求まるということです。
そのためか、scikit-learnのLinearRegressionにはrandom_stateオプションが存在しません。

最小二乗法の結論として、wを求める式は以下になります。

最小二乗法の詳細は、別の記事にて掘り下げようと思います。

LinearRegressionを試してみる

それでは、実際にLinearRegressionを試してみます。
この試しから、最終的にsample_weightオプションの説明まで展開していきます。

先ず、観測点を用意するための以下Pythonコードです。

# 必要なライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# 観測点の生成(等間隔のxに対して、ノイズを乗っけたyを生成)
np.random.seed(0)
x = (np.arange(51) / 50)[:, np.newaxis]
noise = (np.random.rand(51) / 3)[:, np.newaxis]
y = (x * 2) + noise
# --------------------------------------------------
# --------------------------------------------------
# --------------------------------------------------
# x、yと学習によって得た近似直線を描画する
cmap = plt.get_cmap("tab10")
plt.figure(figsize=(12,16), dpi=100)
plt.subplot(2, 1, 1)
plt.grid(which='major', color=[0.7, 0.7, 0.7], linestyle='-')
plt.scatter(x, y, s=300, alpha=0.7, color=cmap(0), label='観測点')
plt.legend(fontsize=15, loc='lower right')
plt.ylim([0, 3.5])
plt.show()

これを実行すると、以下の観測点プロットが得られます。

このデータに対して、LinearRegressionを適用します。
先程のコードに対して、太字部分を追加しました。

# 必要なライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# 観測点の生成(等間隔のxに対して、ノイズを乗っけたyを生成)
np.random.seed(0)
x = (np.arange(51) / 50)[:, np.newaxis]
noise = (np.random.rand(51) / 3)[:, np.newaxis]
y = (x * 2) + noise
# --------------------------------------------------# scikit-learnのsolverによって、近似直線を得る
clf = LinearRegression(fit_intercept=True)
clf.fit(X=x, y=y)
y_hat = clf.predict(x)
# --------------------------------------------------# 最小二乗法の式を解いて、近似直線式の係数を得る
x_ = np.concatenate([x, np.ones(np.shape(x))], axis=1)
w = np.dot(np.linalg.inv(np.dot(x_.T, x_)), np.dot(x_.T, y))
y_hat_ = np.dot(x_, w)
# --------------------------------------------------# x、yと学習によって得た近似直線を描画する
cmap = plt.get_cmap("tab10")
plt.figure(figsize=(12,16), dpi=100)
plt.subplot(2, 1, 1)
plt.grid(which='major', color=[0.7, 0.7, 0.7], linestyle='-')
plt.scatter(x, y, s=300, alpha=0.7, color=cmap(0), label='観測点')
plt.plot(x, y_hat, linewidth=10, alpha=0.7, color=cmap(1), label='近似曲線 by scikit-learn')
plt.plot(x, y_hat_, linewidth=8, alpha=0.3, color=cmap(2), label='近似曲線 by 最小二乗法', linestyle='--')

plt.legend(fontsize=15, loc='lower right')
plt.ylim([0, 3.5])
print('scikit-learnで解いたweight = [%.3f, %.3f]' % (clf.coef_, clf.intercept_))
print('最小二乗法で解いたweight = [%.3f, %.3f]' % (w[0], w[1]))
plt.show()

このコードを実行すると、以下結果が得られます。

観測点に対して、上手いこと近似直線が引かれました。

線を引くまでは、ライブラリを使えば、
① LinearRegressionクラスを定義して…
② X値・y値を入力として、fitメソッドを実行して…
③ X値を入力として、predictメソッドを実行する。
という3ステップで実行できます。

尚、上記の図における、橙色の直線が、3ステップで引いた線です。
併せて、先程紹介した式でwを解いて、引いた直線が緑の点線です。
2つは全く同じ直線を導出できています。

sample_weightオプションは外れ値データを除くために存在する

先程の例は、良い感じに直線が引けました。
次に、イジワルなケースでLinearRegressionを適用してみましょう。

太字が追加コードですが、これが足されることで、異なる様子となります。

# 必要なライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# 観測点の生成(等間隔のxに対して、ノイズを乗っけたyを生成)
np.random.seed(0)
x = (np.arange(51) / 50)[:, np.newaxis]
noise = (np.random.rand(51) / 3)[:, np.newaxis]
y = (x * 2) + noise
y[30:38] = 3.25
# --------------------------------------------------# scikit-learnのsolverによって、近似直線を得る
clf = LinearRegression(fit_intercept=True)
clf.fit(X=x, y=y)
y_hat = clf.predict(x)
# --------------------------------------------------# 最小二乗法の式を解いて、近似直線式の係数を得る
x_ = np.concatenate([x, np.ones(np.shape(x))], axis=1)
w = np.dot(np.linalg.inv(np.dot(x_.T, x_)), np.dot(x_.T, y))
y_hat_ = np.dot(x_, w)
# --------------------------------------------------# x、yと学習によって得た近似直線を描画する
cmap = plt.get_cmap("tab10")
plt.figure(figsize=(12,16), dpi=100)
plt.subplot(2, 1, 1)
plt.grid(which='major', color=[0.7, 0.7, 0.7], linestyle='-')
plt.scatter(x, y, s=300, alpha=0.7, color=cmap(0), label='観測点')
plt.plot(x, y_hat, linewidth=10, alpha=0.7, color=cmap(1), label='近似直線 by scikit-learn')
plt.plot(x, y_hat_, linewidth=8, alpha=0.3, color=cmap(2), label='近似直線 by 最小二乗法', linestyle='--')
plt.legend(fontsize=15, loc='lower right')
plt.ylim([0, 3.5])
print('scikit-learnで解いたweight = [%.3f, %.3f]' % (clf.coef_, clf.intercept_))
print('最小二乗法で解いたweight = [%.3f, %.3f]' % (w[0], w[1]))
plt.show()

以下、実行結果です。

先程の例に外れ値を加えたところ、直線がそれに釣られて上に浮いてしまいました。

仮に、加えられた外れ値が、無視したい例外であった場合、近似直線はそれに引きずられて欲しくないものです。
ですが、LinearRegressionは特別な考慮を加えない限り、引きずられてしまいます。
この現象は、いわゆる「過学習」の一種と言えます。

さて、実際の分析でこのような事象が発生したら、どう対処すればよいでしょうか?
その答えの1つが、sample_weightを用いた対応となります。
sample_weightはサンプルに対して「無視して欲しい度合」を指定できるオプションです。

どう使うかを説明します。
例えば、近似曲線と観測点の誤差を測ってみます。

# 必要なライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# 観測点の生成(等間隔のxに対して、ノイズを乗っけたyを生成)
np.random.seed(0)
x = (np.arange(51) / 50)[:, np.newaxis]
noise = (np.random.rand(51) / 3)[:, np.newaxis]
y = (x * 2) + noise
y[30:38] = 3.25
# --------------------------------------------------# scikit-learnのsolverによって、近似直線を得る
clf = LinearRegression(fit_intercept=True)
clf.fit(X=x, y=y)
y_hat = clf.predict(x)
# 近似直線との誤差を計算する
diff = y - y_hat
# --------------------------------------------------# 最小二乗法の式を解いて、近似直線式の係数を得る
x_ = np.concatenate([x, np.ones(np.shape(x))], axis=1)
w = np.dot(np.linalg.inv(np.dot(x_.T, x_)), np.dot(x_.T, y))
y_hat_ = np.dot(x_, w)
# --------------------------------------------------# x、yと学習によって得た近似直線を描画する
cmap = plt.get_cmap("tab10")
plt.figure(figsize=(12,16), dpi=100)
plt.subplot(2, 1, 1)
plt.grid(which='major', color=[0.7, 0.7, 0.7], linestyle='-')
plt.scatter(x, y, s=300, alpha=0.7, color=cmap(0), label='観測点')
plt.plot(x, y_hat, linewidth=10, alpha=0.7, color=cmap(1), label='近似直線 by scikit-learn')
plt.plot(x, y_hat_, linewidth=8, alpha=0.3, color=cmap(2), label='近似直線 by 最小二乗法', linestyle='--')
for diff_i in range(len(diff)):
if (diff_i == 0):
plt.plot([x[diff_i], x[diff_i]], [y[diff_i], y_hat[diff_i]], linewidth=8, alpha=0.35, color=cmap(3), label='誤差')
else:
plt.plot([x[diff_i], x[diff_i]], [y[diff_i], y_hat[diff_i]], linewidth=8, alpha=0.35, color=cmap(3))
plt.legend(fontsize=15, loc='lower right')
plt.ylim([0, 3.5])
plt.subplot(2, 1, 2)
plt.grid(which='major', color=[0.7, 0.7, 0.7], linestyle='-')
plt.bar(np.array(x.T[0]), np.array(np.abs(diff).T[0]), width=((x[1]-x[0]) * 0.8), alpha=0.35, color=cmap(3), label='誤差(絶対値)')
plt.legend(fontsize=15, loc='upper left')
print('scikit-learnで解いたweight = [%.3f, %.3f]' % (clf.coef_, clf.intercept_))
print('最小二乗法で解いたweight = [%.3f, %.3f]' % (w[0], w[1]))
plt.show()

新たに追加された赤線が、誤差を表しています。
サンプル毎に、誤差に差がありますね。

今回は、この誤差が大きいサンプルほど不要である可能性が高く、誤差が小さいサンプルほど必要である可能性が高い、と仮定してみます。
その度合を、「最大誤差と個別誤差との差の2乗」として、sample_weightに設定してみます。

上記のsample_weight設定を行った上で、近似直線を求めるコードが以下です。

# 必要なライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# 観測点の生成(等間隔のxに対して、ノイズを乗っけたyを生成)
np.random.seed(0)
x = (np.arange(51) / 50)[:, np.newaxis]
noise = (np.random.rand(51) / 3)[:, np.newaxis]
y = (x * 2) + noise
y[30:38] = 3.25
# --------------------------------------------------# scikit-learnのsolverによって、近似直線を得る
clf = LinearRegression(fit_intercept=True)
clf.fit(X=x, y=y)
y_hat = clf.predict(x)
# 近似直線との誤差を計算する
diff = y - y_hat
# 近似直線との誤差が、比較的が大きい点を無視するような、sample_weightを作成する
sample_weight = (np.max(np.abs(diff)) - np.abs(diff)).astype('float32').T[0] ** 2
# scikit-learnのsolverに、sample_weightを与えて、近似直線を得る
clf.fit(X=x, y=y, sample_weight=sample_weight)
y_hat = clf.predict(x)
# 近似直線との誤差を計算する(再)
diff = y - y_hat
# --------------------------------------------------# 最小二乗法の式を解いて、近似直線式の係数を得る
x_ = np.concatenate([x, np.ones(np.shape(x))], axis=1)
w = np.dot(np.linalg.inv(np.dot(x_.T, x_)), np.dot(x_.T, y))
y_hat_ = np.dot(x_, w)
# --------------------------------------------------# x、yと学習によって得た近似直線を描画する
cmap = plt.get_cmap("tab10")
plt.figure(figsize=(12,16), dpi=100)
plt.subplot(2, 1, 1)
plt.grid(which='major', color=[0.7, 0.7, 0.7], linestyle='-')
plt.scatter(x, y, s=300, alpha=0.7, color=cmap(0), label='観測点')
plt.plot(x, y_hat, linewidth=10, alpha=0.7, color=cmap(1), label='近似直線 by scikit-learn')
plt.plot(x, y_hat_, linewidth=8, alpha=0.3, color=cmap(2), label='近似直線 by 最小二乗法', linestyle='--')
for diff_i in range(len(diff)):
if (diff_i == 0):
plt.plot([x[diff_i], x[diff_i]], [y[diff_i], y_hat[diff_i]], linewidth=8, alpha=0.35, color=cmap(3), label='誤差')
else:
plt.plot([x[diff_i], x[diff_i]], [y[diff_i], y_hat[diff_i]], linewidth=8, alpha=0.35, color=cmap(3))
plt.legend(fontsize=15, loc='lower right')
plt.ylim([0, 3.5])
plt.subplot(2, 1, 2)
plt.grid(which='major', color=[0.7, 0.7, 0.7], linestyle='-')
plt.bar(np.array(x.T[0]), np.array(np.abs(diff).T[0]), width=((x[1]-x[0]) * 0.8), alpha=0.35, color=cmap(3), label='誤差(絶対値)')
plt.legend(fontsize=15, loc='upper left')
print('scikit-learnで解いたweight = [%.3f, %.3f]' % (clf.coef_, clf.intercept_))
print('最小二乗法で解いたweight = [%.3f, %.3f]' % (w[0], w[1]))
plt.show()

sample_weightの設定を行った橙色線が、外れ値を無視して近似してくれました。
緑点線は、まだsample_weightの考慮を加えていません。

もし、外れ値をうまく定義できたのなら、役に立ちそうな機能ですね。

具体的に、どうsample_weightを扱っているか?

sample_weightの意義は理解いただけたかと思います。
ここからは、具体的なアルゴリズムの説明を行っていきます。
sample_weightをどう扱うことで、先程のような結果に至っているのか?

それが書かれているLinearRegressionのコードは以下に存在します。

上記コード(或いは、scikit-learnのコード全般)は、X値がSparse型であった時の配慮や、想定外のデータ形式を弾くためのチェック機能等も組まれている為、読むのには若干苦労します。
しかし、シンプルにsample_weightが使われている場所にフォーカスしてい見ていくと、以下の処理が行われていることが分かります。

【sample_weightを用いたwの求め方】
① 合計値がデータ行数となるよう正規化されたsample_weightを用いて、X値とy値の重み付き平均を求める。
X値とy値から、①の値を引く。
③ sample_weightのルート値(2乗根値)を算出する。
④ ③を要素として持つ対角行列を作る。
⑤ ④と、X値・y値との行列積を算出する。
⑥ ⑤で求められたX値・y値から、最小二乗法によって、wを求める。
⑦ ①で求めたX値の重み付き平均と、⑥で算出したwとの内積を求める。
⑧ ①で求めたy値の重み付き平均から、⑦の値を引いて、それをBias値とする。

以上。
結構複雑なステップですね。

1つずつ実施されていることを説明しますと…
①は、sample_weightを考慮して引く近似直線が、必ず通る点(重み付き標本平均点)を計算しています。
②は、X値とy値から①の値を引くことで、分布の原点を①の点としており、そうすることでBiasの考慮を後回しにしています。
③④⑤⑥は、一般的な「重み付き最小二乗法」によるwの算出式を複数プロセスに分けて実施している形です。

(※Dは、sample_weightの値を対角要素に持つ正方対角行列)
⑦⑧は、②の射影をする前の分布について、Biasを計算しています。

実際にコードに書くと、以下となります。

# 必要なライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
import scipy.sparse as sp
from scipy import linalg
from scipy import sparse
from sklearn.utils.extmath import safe_sparse_dot
# 観測点の生成(等間隔のxに対して、ノイズを乗っけたyを生成)
np.random.seed(0)
x = (np.arange(51) / 50)[:, np.newaxis]
noise = (np.random.rand(51) / 3)[:, np.newaxis]
y = (x * 2) + noise
y[30:38] = 3.25
# --------------------------------------------------# scikit-learnのsolverによって、近似直線を得る
clf = LinearRegression(fit_intercept=True)
clf.fit(X=x, y=y)
y_hat = clf.predict(x)
# 近似直線との誤差を計算する
diff = y - y_hat
# 近似直線との誤差が、比較的が大きい点を無視するような、sample_weightを作成する
sample_weight = (np.max(np.abs(diff)) - np.abs(diff)).astype('float32').T[0] ** 2
# scikit-learnのsolverに、sample_weightを与えて、近似直線を得る
clf.fit(X=x, y=y, sample_weight=sample_weight)
y_hat = clf.predict(x)
# 近似直線との誤差を計算する
diff = y - y_hat
# --------------------------------------------------# 最小二乗法の式を解いて、近似直線式の係数を得る
x_ = np.concatenate([x, np.ones(np.shape(x))], axis=1)
# ① 合計値がデータ行数となるよう正規化されたsample_weightを用いて、X値とy値の重み付き平均を求める
x_offset = np.mean(x.ravel() * sample_weight / np.sum(sample_weight) * len(x))
y_offset = np.mean(y.ravel() * sample_weight / np.sum(sample_weight) * len(x))
# ② X値とy値から、①の値を引く
x__ = (x - x_offset).copy()
y__ = (y - y_offset).copy()
# ③ sample_weightのルート値(2乗根値)を算出する
sample_weight_ = np.sqrt(sample_weight)
# ④ ③を要素として持つ対角行列を作る
sample_weight__ = np.zeros([len(x__), len(x__)], dtype='float32')
for sample_i in range(len(x__)):
sample_weight__[sample_i, sample_i] = sample_weight_[sample_i]
# ⑤ ④と、X値・y値との行列積を算出する
x___ = np.dot(sample_weight__, x__)
y___ = np.dot(sample_weight__, y__)
# ⑥ ⑤で求められたX値・y値から、最小二乗法によって、wを求める
w = np.dot(np.linalg.inv(np.dot(x___.T, x___)), np.dot(x___.T, y___))
# ⑦ ①で求めたX値の重み付き平均と、⑥で算出したwとの内積を求める
y_offset_ = np.dot(x_offset, w.T)
# ⑧ ①で求めたy値の重み付き平均から、⑦の値を引いて、それをBias値とする
w = np.concatenate([w, y_offset - y_offset_])
y_hat_ = np.dot(x_, w)
# --------------------------------------------------# x、yと学習によって得た近似直線を描画する
cmap = plt.get_cmap("tab10")
plt.figure(figsize=(12,16), dpi=100)
plt.subplot(2, 1, 1)
plt.grid(which='major', color=[0.7, 0.7, 0.7], linestyle='-')
plt.scatter(x, y, s=300, alpha=0.7, color=cmap(0), label='観測点')
plt.plot(x, y_hat, linewidth=10, alpha=0.7, color=cmap(1), label='近似直線 by scikit-learn')
plt.plot(x, y_hat_, linewidth=8, alpha=0.3, color=cmap(2), label='近似直線 by 最小二乗法', linestyle='--')
for diff_i in range(len(diff)):
if (diff_i == 0):
plt.plot([x[diff_i], x[diff_i]], [y[diff_i], y_hat[diff_i]], linewidth=8, alpha=0.35, color=cmap(3), label='誤差')
else:
plt.plot([x[diff_i], x[diff_i]], [y[diff_i], y_hat[diff_i]], linewidth=8, alpha=0.35, color=cmap(3))
plt.legend(fontsize=15, loc='lower right')
plt.ylim([0, 3.5])
plt.subplot(2, 1, 2)
plt.grid(which='major', color=[0.7, 0.7, 0.7], linestyle='-')
plt.bar(np.array(x.T[0]), np.array(np.abs(diff).T[0]), width=((x[1]-x[0]) * 0.8), alpha=0.35, color=cmap(3), label='誤差(絶対値)')
plt.legend(fontsize=15, loc='upper left')
print('scikit-learnで解いたweight = [%.3f, %.3f]' % (clf.coef_, clf.intercept_))
print('最小二乗法で解いたweight = [%.3f, %.3f]' % (w[0], w[1]))
plt.show()

sample_weightを用いたLinearRegressionの結果と、その処理をリバースしたコード(太字部分)の結果が合致しました。

更に、コードをシンプルにしますと、以下となります。

# 必要なライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
import scipy.sparse as sp
from scipy import linalg
from scipy import sparse
from sklearn.utils.extmath import safe_sparse_dot
# 観測点の生成(等間隔のxに対して、ノイズを乗っけたyを生成)
np.random.seed(0)
x = (np.arange(51) / 50)[:, np.newaxis]
noise = (np.random.rand(51) / 3)[:, np.newaxis]
y = (x * 2) + noise
y[30:38] = 3.25
# --------------------------------------------------# scikit-learnのsolverによって、近似直線を得る
clf = LinearRegression(fit_intercept=True)
clf.fit(X=x, y=y)
y_hat = clf.predict(x)
# 近似直線との誤差を計算する
diff = y - y_hat
# 近似直線との誤差が、比較的が大きい点を無視するような、sample_weightを作成する
sample_weight = (np.max(np.abs(diff)) - np.abs(diff)).astype('float32').T[0] ** 2
# scikit-learnのsolverに、sample_weightを与えて、近似直線を得る
clf.fit(X=x, y=y, sample_weight=sample_weight)
y_hat = clf.predict(x)
# 近似直線との誤差を計算する
diff = y - y_hat
# --------------------------------------------------# XにBias列を付与
x_ = np.concatenate([x, np.ones(np.shape(x))], axis=1)
# sample_weightを対角要素とする正方対角行列を生成
sample_weight__ = np.zeros([len(x__), len(x__)], dtype='float32')
for sample_i in range(len(x__)):
sample_weight__[sample_i, sample_i] = sample_weight[sample_i]
# 重み付き最小二乗法の式を解いて、近似直線式の係数を得る(※この時、Biasは付与しない)
w = np.dot(np.linalg.inv(np.dot(np.dot(x_.T, sample_weight__), x_)), np.dot(np.dot(x_.T, sample_weight__), y))
# 予測値を計算
y_hat_ = np.dot(x_, w)
# --------------------------------------------------# x、yと学習によって得た近似直線を描画する
cmap = plt.get_cmap("tab10")
plt.figure(figsize=(12,16), dpi=100)
plt.subplot(2, 1, 1)
plt.grid(which='major', color=[0.7, 0.7, 0.7], linestyle='-')
plt.scatter(x, y, s=300, alpha=0.7, color=cmap(0), label='観測点')
plt.plot(x, y_hat, linewidth=10, alpha=0.7, color=cmap(1), label='近似直線 by scikit-learn')
plt.plot(x, y_hat_, linewidth=8, alpha=0.3, color=cmap(2), label='近似直線 by 最小二乗法', linestyle='--')
for diff_i in range(len(diff)):
if (diff_i == 0):
plt.plot([x[diff_i], x[diff_i]], [y[diff_i], y_hat[diff_i]], linewidth=8, alpha=0.35, color=cmap(3), label='誤差')
else:
plt.plot([x[diff_i], x[diff_i]], [y[diff_i], y_hat[diff_i]], linewidth=8, alpha=0.35, color=cmap(3))
plt.legend(fontsize=15, loc='lower right')
plt.ylim([0, 3.5])
plt.subplot(2, 1, 2)
plt.grid(which='major', color=[0.7, 0.7, 0.7], linestyle='-')
plt.bar(np.array(x.T[0]), np.array(np.abs(diff).T[0]), width=((x[1]-x[0]) * 0.8), alpha=0.35, color=cmap(3), label='誤差(絶対値)')
plt.legend(fontsize=15, loc='upper left')
print('scikit-learnで解いたweight = [%.3f, %.3f]' % (clf.coef_, clf.intercept_))
print('最小二乗法で解いたweight = [%.3f, %.3f]' % (w[0], w[1]))
plt.show()

シンプルなコードでも同じ結果が得られました。
要するに、一般的な「重み付き最小二乗法」が行われている、という結論がキッチリ数値で確認できました。
気持ちいいですね〜。

ちなみに、MATLABのドキュメンテーションにも同じ式が書かれていました。

以上が、LinearRegressionにおけるsample_weightオプションのメカニズムの説明となります。
リバースして、具体的な処理を把握すると、ライブラリとの距離が近くなった気がしないでしょうか?

おわりに

アルゴリズムの詳細が知りたいときは、GitHubコードをリバースすると良いですよ、というススメでした。
具体的、かつ、正確に理解ができます。

こんな感じで、今後もアルゴリズムのリバースを行っていこうと思います。

--

--