必要なライブラリを最初に読み込んでおきます.Pandas, Matplotlib 以外に,NumPy と最適化を行うための SciPy ライブラリをインポートします.
import numpy as np
import pandas as pd
import scipy.optimize as optimize # 最適化
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize # contour の色指定
# 高解像度ディスプレイ用
from IPython.display import set_matplotlib_formats
# from matplotlib_inline.backend_inline import set_matplotlib_formats # バージョンによってはこちらを有効に
set_matplotlib_formats('retina')
CSVファイルを読み込んで,NumPy配列に変換し,これから散布図を描いてみます.
# CSVファイルを読み込む
url = "https://github.com/rinsaka/sample-data-sets/blob/master/lr.csv?raw=true"
df = pd.read_csv(url)
# NumPy 配列に変換する
x_data = df.loc[:, 'x'].values
y_data = df.loc[:, 'y'].values
# 散布図を作成する
fig, ax = plt.subplots(1, 1, figsize=(6, 4))
ax.scatter(x_data, y_data)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_xlim(0,10)
ax.set_ylim(0,10)
# plt.savefig('lr-data.png', dpi=300, facecolor='white')
plt.show()
線形回帰を行いたいので,その回帰式を定義します.このページでは最も単純な一次関数を利用します.
上の式で \(a\) は切片,\(b\) は直線の傾きです.ここでは,x
の値(またはリスト)とパラメータ w = [a, b]
を与えれば,y
の値(またはリスト)を返す関数を次のように定義します.
関数の定義
"""
関数の定義
"""
def my_func(w, x):
y = w[0] + w[1] * x
return y
定義した関数 my_func
に \(a = 1\),\(b = 2\),\(x = 3\) を代入して \(y\) の値(つまり,\(1 + 2 \times 3\) )を求めてみます.
my_func([1, 2], 3)
7
定義した関数の引数 x
に NumPy 配列を与えれば,計算結果が NumPy 配列で得られることに注意します.(つまり,繰り返して計算する必要がない.)
my_func([1, 2], np.array([0, 1, 2]))
array([1, 3, 5])
回帰直線のグラフを描きたいので,まず x
の値を格納する NumPy 配列 x_plot
を準備します.このモデルの回帰式は直線であるので,始点と終点の2点のデータがあれば十分ですが,次のページ以降では曲線となる回帰式も描きたいので,等間隔で100点のデータを準備しておきます.
x のNumPy配列 x_plot を準備する
x_plot = np.linspace(0, 10, 100)
print(x_plot)
[ 0. 0.1010101 0.2020202 0.3030303 0.4040404 0.50505051 0.60606061 0.70707071 0.80808081 0.90909091 1.01010101 1.11111111 1.21212121 1.31313131 1.41414141 1.51515152 1.61616162 1.71717172 1.81818182 1.91919192 2.02020202 2.12121212 2.22222222 2.32323232 2.42424242 2.52525253 2.62626263 2.72727273 2.82828283 2.92929293 3.03030303 3.13131313 3.23232323 3.33333333 3.43434343 3.53535354 3.63636364 3.73737374 3.83838384 3.93939394 4.04040404 4.14141414 4.24242424 4.34343434 4.44444444 4.54545455 4.64646465 4.74747475 4.84848485 4.94949495 5.05050505 5.15151515 5.25252525 5.35353535 5.45454545 5.55555556 5.65656566 5.75757576 5.85858586 5.95959596 6.06060606 6.16161616 6.26262626 6.36363636 6.46464646 6.56565657 6.66666667 6.76767677 6.86868687 6.96969697 7.07070707 7.17171717 7.27272727 7.37373737 7.47474747 7.57575758 7.67676768 7.77777778 7.87878788 7.97979798 8.08080808 8.18181818 8.28282828 8.38383838 8.48484848 8.58585859 8.68686869 8.78787879 8.88888889 8.98989899 9.09090909 9.19191919 9.29292929 9.39393939 9.49494949 9.5959596 9.6969697 9.7979798 9.8989899 10. ]
いま準備した NumPy 配列 x_plot
に対応する y
の値(NumPy配列)を計算します.まずは,繰り返しを用いた C言語風の記述方法を使ってみます.
C言語風の書き方
# 傾きと切片のリストを準備
w = np.array([4, 0.25])
# y_pred のリストを準備(ゼロで初期化)
y_pred = np.zeros(len(x_plot))
# 配列x_plotから一つずつ取り出して,y_predを計算する
for i, x in enumerate(x_plot):
y_pred[i] = my_func(w, x)
print(y_pred)
[4. 4.02525253 4.05050505 4.07575758 4.1010101 4.12626263 4.15151515 4.17676768 4.2020202 4.22727273 4.25252525 4.27777778 4.3030303 4.32828283 4.35353535 4.37878788 4.4040404 4.42929293 4.45454545 4.47979798 4.50505051 4.53030303 4.55555556 4.58080808 4.60606061 4.63131313 4.65656566 4.68181818 4.70707071 4.73232323 4.75757576 4.78282828 4.80808081 4.83333333 4.85858586 4.88383838 4.90909091 4.93434343 4.95959596 4.98484848 5.01010101 5.03535354 5.06060606 5.08585859 5.11111111 5.13636364 5.16161616 5.18686869 5.21212121 5.23737374 5.26262626 5.28787879 5.31313131 5.33838384 5.36363636 5.38888889 5.41414141 5.43939394 5.46464646 5.48989899 5.51515152 5.54040404 5.56565657 5.59090909 5.61616162 5.64141414 5.66666667 5.69191919 5.71717172 5.74242424 5.76767677 5.79292929 5.81818182 5.84343434 5.86868687 5.89393939 5.91919192 5.94444444 5.96969697 5.99494949 6.02020202 6.04545455 6.07070707 6.0959596 6.12121212 6.14646465 6.17171717 6.1969697 6.22222222 6.24747475 6.27272727 6.2979798 6.32323232 6.34848485 6.37373737 6.3989899 6.42424242 6.44949495 6.47474747 6.5 ]
しかしながら,定義した関数 my_func
に NumPy 配列を与えれば,すべての結果を計算できるので,Python では次のような書き方ができます.まだ試していませんが,この書き方の方が実行速度の面でも有利なはずです.
切片 4, 傾き 0.25 として,回帰直線のデータを作成する
# 傾きと切片のリストを準備
w = np.array([4, 0.25])
# NumPy配列では一気に計算できる
y_pred = my_func(w, x_plot)
print(y_pred)
[4. 4.02525253 4.05050505 4.07575758 4.1010101 4.12626263 4.15151515 4.17676768 4.2020202 4.22727273 4.25252525 4.27777778 4.3030303 4.32828283 4.35353535 4.37878788 4.4040404 4.42929293 4.45454545 4.47979798 4.50505051 4.53030303 4.55555556 4.58080808 4.60606061 4.63131313 4.65656566 4.68181818 4.70707071 4.73232323 4.75757576 4.78282828 4.80808081 4.83333333 4.85858586 4.88383838 4.90909091 4.93434343 4.95959596 4.98484848 5.01010101 5.03535354 5.06060606 5.08585859 5.11111111 5.13636364 5.16161616 5.18686869 5.21212121 5.23737374 5.26262626 5.28787879 5.31313131 5.33838384 5.36363636 5.38888889 5.41414141 5.43939394 5.46464646 5.48989899 5.51515152 5.54040404 5.56565657 5.59090909 5.61616162 5.64141414 5.66666667 5.69191919 5.71717172 5.74242424 5.76767677 5.79292929 5.81818182 5.84343434 5.86868687 5.89393939 5.91919192 5.94444444 5.96969697 5.99494949 6.02020202 6.04545455 6.07070707 6.0959596 6.12121212 6.14646465 6.17171717 6.1969697 6.22222222 6.24747475 6.27272727 6.2979798 6.32323232 6.34848485 6.37373737 6.3989899 6.42424242 6.44949495 6.47474747 6.5 ]
回帰直線のデータが作成できたので,散布図に重ねてみよう.
fig, ax = plt.subplots(1, 1, figsize=(6, 4))
ax.scatter(x_data, y_data)
ax.plot(x_plot, y_pred)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_xlim(0,10)
ax.set_ylim(0,10)
# plt.savefig('lr-model1-1.png', dpi=300, facecolor='white')
plt.show()
あるパラメータ w = [a, b]
を与えて計算した回帰直線と一つのデータとの2乗誤差を計算してみよう.まずは2乗誤差を求める関数 get_square_error
を定義します.
def get_square_error(w, x, y):
y_pred = my_func(w, x)
error = (y - y_pred)**2
return error
上と同じパラメータの組み合わせ(w = [4, 2.5]
)のときに,先頭のデータ x = 0.1
, y = 4.0
に対する予測値は 4.025
となり,その2乗誤差は 0.0006250000000000178
となることがわかります.
w = np.array([4, 0.25])
print(x_data[0], y_data[0], my_func(w, x_data[0]))
print(get_square_error(w, x_data[0], y_data[0]))
0.1 4.0 4.025 0.0006250000000000178
次のデータ x = 0.2
, y = 4.8
に対する予測値は 4.05
となるので,その2乗誤差は 0.5625
です.
print(x_data[1], y_data[1], my_func(w, x_data[1]))
print(get_square_error(w, x_data[1], y_data[1]))
0.2 4.8 4.05 0.5625
さらに別のデータ x = 7.2
, y = 1.0
に対する予測値は 5.8
となるので,その2乗誤差は大きくなり 23.04
です.
print(x_data[34], y_data[34], my_func(w, x_data[34]))
print(get_square_error(w, x_data[34], y_data[34]))
7.2 1.0 5.8 23.04
上の手順では,1つのデータに対する2乗誤差を計算した.次は,すべてのデータについての2乗誤差の総和,つまり,残差2乗和 (rss: residual sum-of-squares) を計算する関数を定義しよう.Python では NumPy を使うと非常に短いコードで計算が可能になります.つまり for
や while
等の繰り返し処理を必要としない.
def get_rss(w, x, y):
y_pred = my_func(w, x)
error = (y - y_pred)**2
return np.sum(error)
上と同じパラメータの組み合わせ(w = [4, 2.5]
)のときの残差2乗和を計算すると rss = 331.03
となった.
w = np.array([4, 0.25])
rss = get_rss(w, x_data, y_data)
print(rss)
331.031875
別のパラメータの組み合わせ(w = [5, -0.25]
)に対しては rss = 261.04
となり,残差2乗和が減少した.
w = np.array([5, -0.25])
rss = get_rss(w, x_data, y_data)
print(rss)
261.041875
上の2つの組み合わせでの回帰直線と散布図を重ね合わせると次のようになります.
y_pred1 = my_func([4, 0.25], x_plot)
y_pred2 = my_func([5, -0.25], x_plot)
fig, ax = plt.subplots(1, 1, figsize=(6, 4))
ax.scatter(x_data, y_data, label="data")
ax.plot(x_plot, y_pred1, label="y = 4 + 0.25x")
ax.plot(x_plot, y_pred2, label="y = 5 - 0.25x")
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_xlim(0,10)
ax.set_ylim(0,10)
ax.legend()
# plt.savefig('lr-model1-2.png', dpi=300, facecolor='white')
plt.show()
つまり,問題は残差2乗和 (rss) が最小になるようなパラメータ(切片,傾き)の組み合わせを求めることです.単回帰分析では連立方程式を解析的に解くことによって最適なパラメータを求めることが可能ですが,次ページ以降のモデルに対応するために,次のステップで最適化のイメージを掴んだ後に,さらに次のステップでは代表的な非線形最適化手法(ネルダーミード法,BFGS法)を使って求めてみます.
上の例では,\(y = 4 + 0.25x\) と \(y = 5 -0.25x\) での残差2乗和を比較した.問題は \(y = a + bx\) について,残差2乗和が最も小さくなるような \(a\) と \(b\) を求めることです.
最適化のイメージを掴むために,まずは傾きを \(b = 0.25\) に固定した状態で,様々な \(a\) について rss を求めたものが下のグラフです.もしも \(b=0.25\) ならば,図から \(a=3.4\) あたりで rss が最小になることがわかります.これは上の図で傾きが 0.25 の直線を上下に平行移動したときに,切片が 3.4 になる付近で rss が最小になることを意味します.つまり,上の図で青の直線は(傾きを変化させないのであれば)もう少し下に移動したほうが良いということです.
a_list = np.linspace(1,8,500) # 傾き a を変化させる
b = 0.25 # 切片 b は固定
rss = np.zeros(a_list.shape[0]) # ゼロで初期化
for i, a in enumerate(a_list):
rss[i] = get_rss([a, b], x_data, y_data)
fig, ax = plt.subplots(1, 1, figsize=(6, 4))
ax.plot(a_list, rss, label="b = 0.25")
ax.set_xlabel('a')
ax.set_ylabel('rss')
ax.set_xlim(1,8)
ax.set_ylim(200,500)
ax.legend()
# plt.savefig('lr-rss-1.png', dpi=300, facecolor='white')
plt.show()
さらに,4通りの傾きについて同じ様に rss を求めたものが下のグラフです.この図から \(b = -0.25\) 付近で rss が最も小さくなりそうであることが伺える.このときの切片は \(a = 5.6\) 程度になることもわかります.
a_list = np.linspace(1,8,500) # 傾き a を変化させる
b_list = np.array([-0.50, -0.25, 0.00, 0.25]) # 切片 b も変化させる
rss = np.zeros((a_list.shape[0], b_list.shape[0])) # ゼロで初期化
for j, b in enumerate(b_list):
for i, a in enumerate(a_list):
rss[i, j] = get_rss([a, b], x_data, y_data)
fig, ax = plt.subplots(1, 1, figsize=(6, 4))
for j, b in enumerate(b_list):
ax.plot(a_list, rss[:,j], label=f"b = {b}")
ax.set_xlabel('a')
ax.set_ylabel('rss')
ax.set_xlim(1,8)
ax.set_ylim(200,500)
ax.legend()
# plt.savefig('lr-rss-2.png', dpi=300, facecolor='white')
plt.show()
今度は,切片を \(a = 6\) に固定したときに,最適な傾きがどの程度になるかを考えます.下の図から傾きが \(b = -0.3\) 付近で rss が最小化されます.
a = 6
b_list = np.linspace(-0.8, 0.5, 500)
rss = np.zeros(b_list.shape[0]) # ゼロで初期化
for j, b in enumerate(b_list):
rss[j] = get_rss([a, b], x_data, y_data)
fig, ax = plt.subplots(1, 1, figsize=(6, 4))
ax.plot(b_list, rss, label="a = 6")
ax.set_xlabel('b')
ax.set_ylabel('rss')
ax.set_xlim(-.8, .5)
ax.set_ylim(200,500)
ax.legend()
# plt.savefig('lr-rss-3.png', dpi=300, facecolor='white')
plt.show()
さらに,様々な切片 \(a\) について同様の計算を行うと,次のようなグラフが得られる.やはりこのグラフからも \(a = 6\),\(b = -0.3\) の周辺に rss を最小化する最適解がありそうであることが伺える.
a_list = np.array([2, 4, 6, 8])
b_list = np.linspace(-0.8, 0.5, 500)
rss = np.zeros((b_list.shape[0], a_list.shape[0])) # ゼロで初期化
for i, a in enumerate(a_list):
for j, b in enumerate(b_list):
rss[j, i] = get_rss([a, b], x_data, y_data)
fig, ax = plt.subplots(1, 1, figsize=(6, 4))
for j, a in enumerate(a_list):
ax.plot(b_list, rss[:,j], label=f"a = {a}")
ax.set_xlabel('b')
ax.set_ylabel('rss')
ax.set_xlim(-.8, .5)
ax.set_ylim(200,500)
ax.legend()
# plt.savefig('lr-rss-4.png', dpi=300, facecolor='white')
plt.show()
最後に,横軸に切片 \(a\),縦軸に傾き \(b\) をとり,rss の大小を色の変化によってプロットしたものが下のグラフです.やはりこの図からも \(a = 5.5\),\(b = -0.25\) の付近に最適解が存在しそうなことが読み取れる.このように,未知パラメータが1個や2個の場合には,グラフを描くことで最適解を探索するイメージを掴むことができます.未知パラメータが3個以上になると図では表現できず,最適解を見つけることは難しくなります.しかしながら,ネルダーミード法や準ニュートン法(BFGS法)などのよく知られた最適化アルゴリズムを用いれば未知パラメータの数が多くなっても最適解を比較的簡単に発見可能です.特にPythonではSciPy ライブラリなどこのような最適化が可能なライブラリが複数存在し,無料で利用できます.(さらに,有料の商用ソルバーを使えばより大規模な問題を高速・高精度に解くこともできる(らしい).)
aa, bb = np.meshgrid(np.linspace(4.0, 7.0, 300), np.linspace(-0.5, 0.0, 300))
rss = np.zeros(aa.ravel().shape[0])
for i in range(aa.ravel().shape[0]):
rss[i] = get_rss([aa.ravel()[i], bb.ravel()[i]], x_data, y_data)
rss = rss.reshape(aa.shape)
fig, ax = plt.subplots(1, 1, figsize=(6, 4))
ax.contourf(aa, bb, rss)
mappable0 = ax.pcolormesh(aa, bb, rss, cmap='gist_rainbow', norm=Normalize(vmin=246, vmax=255))
fig.colorbar(mappable0, ax=ax, orientation="vertical")
ax.set_xlabel('a')
ax.set_ylabel('b')
ax.set_xlim(4.5,6.5)
ax.set_ylim(-0.5,0.0)
# plt.savefig('lr-rss-5.png', dpi=300, facecolor='white')
plt.show()
ここでは代表的な非線形最適化手法(ネルダーミード法,BFGS法)を使ってパラメータの最適化を行います.Python の SciPy ライブラリには強力な最適化関数が実装されているのでこれを用いれば比較的簡単に最適化が行なえます.なおこのページでは最適化のパラメータ数はわずか2個,次のページ以降の他のモデルでも5個程度ですが,ディープラーニングでは数千万や数億を超えるパラメータ数の最適化問題が解かれています.ここで説明している単純な文字認識でもおよそ3万個のパラメータです.
まず,SciPyライブラリがプログラムの先頭でインポートされていることを確認します.インポートされていなければ次の行を追加して実行してください.
import scipy.optimize as optimize # 最適化
SciPy の optimize.minimze 関数を使ってネルダーミード法による最適化を行います.optimize.minimize
関数の引数には
を指定します.今回は残差2乗和を最小化したいので,第1引数には rss
を指定します.第2引数には探索したいパラメータに初期値を与える必要があります.初期値が異なれば別の局所解が得られたり,最適解を得られなかったりするので,問題の種類によっては試行錯誤的に適切な初期値を選ぶ必要があります.しかしながら,この問題ではそれほど気にする必要はないと思われます.目的関数である rss
には最適化したいパラメータ w
以外に x
とy
を与える必要があるので,第3引数には args=(x_data,y_data)
のようにタプル形式で指定します.第4引数には利用したい最適化アルゴリズムを指定します.
w = np.array([0.1, 0.1]) # 初期値を設定
results_nm = optimize.minimize(get_rss, w, args=(x_data, y_data), method='Nelder-Mead')
print(results_nm)
final_simplex: (array([[ 5.59136809, -0.24365817], [ 5.59141949, -0.24366314], [ 5.59145674, -0.24367417]]), array([244.84285679, 244.8428568 , 244.84285681])) fun: 244.84285679273873 message: 'Optimization terminated successfully.' nfev: 121 nit: 62 status: 0 success: True x: array([ 5.59136809, -0.24365817])
上のコードを実行した結果,目的関数値 (fun) が 244.84
となり,最適化が成功した (message: 'Optimization terminated successfully' および success: True) ことがわかります.最適解となるパラメータは x: [ 5.59136809, -0.24365817]
になります.
最適化を行った結果から,目的関数値やパラメータは次のように取得できます.
目的関数値
print(results_nm["fun"])
244.84285679273873
最適解
print(results_nm["x"])
[ 5.59136809 -0.24365817]
切片の最適解
print(results_nm["x"][0])
5.591368093953666
傾きの最適解
print(results_nm["x"][1])
-0.24365816990138328
次に,ブロイデン・フレッチャー・ゴールドファーブ・シャンノ法(BFGS法)によって同じ問題を最適化してみると,ほぼ同じ結果が得られました.
w = np.array([0.1, 0.1]) # 初期値を設定
results_bfgs = optimize.minimize(get_rss, w, args=(x_data, y_data), method='BFGS')
print(results_bfgs)
fun: 244.8428567809353 hess_inv: array([[ 0.05204723, -0.00864194], [-0.00864194, 0.00186052]]) jac: array([1.90734863e-06, 3.81469727e-06]) message: 'Optimization terminated successfully.' nfev: 21 nit: 6 njev: 7 status: 0 success: True x: array([ 5.591403 , -0.24366441])
ネルダーミード法によって得られた結果と散布図を重ねてみよう.このようなデータに直線を当てはめることには予想通り無理がありそうです.
x_plot = np.linspace(0, 10, 100)
y_pred = my_func(results_nm["x"], x_plot)
fig, ax = plt.subplots(1, 1, figsize=(6, 4))
ax.scatter(x_data, y_data, label="data")
ax.plot(x_plot, y_pred, label='model 1')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_xlim(0,10)
ax.set_ylim(0,10)
ax.legend()
# plt.savefig('lr-model1-3.png', dpi=300, facecolor='white')
plt.show()
いま残差2乗和の最小化によって得られた結果と,ここで説明した StatsModels による単回帰分析と同じ方法で得られる結果を比較しておこう.どちらの方法でもほぼ同じ結果(切片と傾き)が得られていることが確認できました.
import statsmodels.api as sm
x = sm.add_constant(x_data)
model = sm.OLS(y_data, x)
results = model.fit()
print(results.params)
[ 5.59140282 -0.24366437]
当てはまりの良さを R-squared や Adj. R-squared の値から確認します.これらの値が非常に小さいことから,やはりこのデータに対して単回帰分析による直線の当てはめは適切な方法でないことがわかります.
print(results.summary2())
Results: Ordinary least squares ================================================================= Model: OLS Adj. R-squared: 0.038 Dependent Variable: y AIC: 197.2346 Date: 2021-07-17 16:39 BIC: 200.7099 No. Observations: 42 Log-Likelihood: -96.617 Df Model: 1 F-statistic: 2.607 Df Residuals: 40 Prob (F-statistic): 0.114 R-squared: 0.061 Scale: 6.1211 ------------------------------------------------------------------- Coef. Std.Err. t P>|t| [0.025 0.975] ------------------------------------------------------------------- const 5.5914 0.7982 7.0049 0.0000 3.9782 7.2047 x1 -0.2437 0.1509 -1.6147 0.1142 -0.5487 0.0613 ----------------------------------------------------------------- Omnibus: 7.545 Durbin-Watson: 0.377 Prob(Omnibus): 0.023 Jarque-Bera (JB): 4.056 Skew: 0.550 Prob(JB): 0.132 Kurtosis: 1.949 Condition No.: 11 =================================================================
不要な部分を削除した後のコード全体は次のような感じになります.
import numpy as np
import pandas as pd
import scipy.optimize as optimize # 最適化
import matplotlib.pyplot as plt
# 高解像度ディスプレイ用
from IPython.display import set_matplotlib_formats
# from matplotlib_inline.backend_inline import set_matplotlib_formats # バージョンによってはこちらを有効に
set_matplotlib_formats('retina')
"""
関数の定義
"""
def my_func(w, x):
y = w[0] + w[1] * x
return y
"""
残差2乗和を求める関数
Residual Sum-of-Squares
"""
def get_rss(w, x, y):
y_pred = my_func(w, x)
error = (y - y_pred)**2
return np.sum(error)
# CSV ファイルを読み込む
url = "https://github.com/rinsaka/sample-data-sets/blob/master/lr.csv?raw=true"
df = pd.read_csv(url)
# NumPy 配列に変換する
x_data = df.loc[:, 'x'].values
y_data = df.loc[:, 'y'].values
# ネルダーミード法による最適化を行う
w = np.array([0.1, 0.1]) # 初期値を設定
results_nm = optimize.minimize(get_rss, w, args=(x_data, y_data), method='Nelder-Mead')
print(results_nm)
# 最適解を使って回帰直線のデータを作成する
x_plot = np.linspace(0, 10, 100)
y_pred = my_func(results_nm["x"], x_plot)
# グラフを描く
fig, ax = plt.subplots(1, 1, figsize=(6, 4))
ax.scatter(x_data, y_data, label="data")
ax.plot(x_plot, y_pred, label='model 1')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_xlim(0,10)
ax.set_ylim(0,10)
ax.legend()
# plt.savefig('lr-model1-3.png', dpi=300, facecolor='white')
plt.show()
final_simplex: (array([[ 5.59136809, -0.24365817], [ 5.59141949, -0.24366314], [ 5.59145674, -0.24367417]]), array([244.84285679, 244.8428568 , 244.84285681])) fun: 244.84285679273873 message: 'Optimization terminated successfully.' nfev: 121 nit: 62 status: 0 success: True x: array([ 5.59136809, -0.24365817])
次のページからは曲線を当てはめるいくつかの方法を試していこう.