web-dev-qa-db-ja.com

scipy、numpy、pythonなどによるシグモイド回帰

互いにややシグモイド関係にある2つの変数(xとy)があり、xの値が与えられた場合に、yの値を予測できるような予測式を見つける必要があります。私の予測式は、2つの変数間のややシグモイド関係を示す必要があります。したがって、線を生成する線形回帰方程式を決定することはできません。 2つの変数のグラフの左右両方で発生する、勾配の緩やかな曲線変化を確認する必要があります。

曲線回帰とPythonをグーグルで調べた後、numpy.polyfitを使い始めましたが、以下のコードを実行すると、ひどい結果が得られました。 必要なタイプのシグモイド回帰方程式を取得するために、以下のコードを書き直す方法を教えてもらえますか?

以下のコードを実行すると、下向きの放物線が表示されることがわかります。これは、変数間の関係がどのように見えるかではありません。代わりに、2つの変数の間にシグモイド関係がもっとあるはずですが、以下のコードで使用しているデータと密接に適合しています。以下のコードのデータは、大規模なサンプルの調査研究からの平均であるため、5つのデータポイントが示唆するよりも多くの統計的検出力を備えています。大規模なサンプルの調査研究からの実際のデータはありませんが、以下の平均とそれらの標準偏差(表示していません)はあります。以下にリストされている平均データを使用して単純な関数をプロットすることをお勧めしますが、複雑さが大幅な改善をもたらす場合、コードはより複雑になる可能性があります。

コードを変更して、シグモイド関数に最適なものを表示するにはどうすればよいですか?できればscipy、numpy、pythonを使用しますか?これが私のコードの現在のバージョンであり、修正する必要があります:

import numpy as np
import matplotlib.pyplot as plt

# Create numpy data arrays
x = np.array([821,576,473,377,326])
y = np.array([255,235,208,166,157])

# Use polyfit and poly1d to create the regression equation
z = np.polyfit(x, y, 3)
p = np.poly1d(z)
xp = np.linspace(100, 1600, 1500)
pxp=p(xp)

# Plot the results
plt.plot(x, y, '.', xp, pxp, '-')
plt.ylim(140,310)
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True)
plt.show()

以下の編集:(質問を再構成)

あなたの反応とそのスピードはとても印象的です。ありがとう、うぬぶ。しかし、より有効な結果を生成するには、データ値を再構成する必要があります。これは、x値を最大x値のパーセンテージとして再キャストし、y値を元のデータのx値のパーセンテージとして再キャストすることを意味します。私はあなたのコードでこれをやろうとしました、そして次のことを思いつきました:

import numpy as np 
import matplotlib.pyplot as plt 
import scipy.optimize 

# Create numpy data arrays 
'''
# Comment out original data
#x = np.array([821,576,473,377,326]) 
#y = np.array([255,235,208,166,157]) 
'''

# Re-calculate x values as a percentage of the first (maximum)
# original x value above
x = np.array([1.000,0.702,0.576,0.459,0.397])

# Recalculate y values as a percentage of their respective x values
# from original data above
y = np.array([0.311,0.408,0.440,0.440,0.482])

def sigmoid(p,x): 
    x0,y0,c,k=p 
    y = c / (1 + np.exp(-k*(x-x0))) + y0 
    return y 

def residuals(p,x,y): 
    return y - sigmoid(p,x) 

p_guess=(600,200,100,0.01) 
(p,  
 cov,  
 infodict,  
 mesg,  
 ier)=scipy.optimize.leastsq(residuals,p_guess,args=(x,y),full_output=1,warning=True)  

'''
# comment out original xp to allow for better scaling of
# new values
#xp = np.linspace(100, 1600, 1500) 
'''

xp = np.linspace(0, 1.1, 1100) 
pxp=sigmoid(p,xp) 

x0,y0,c,k=p 
print('''\ 
x0 = {x0}
y0 = {y0}
c = {c}
k = {k}
'''.format(x0=x0,y0=y0,c=c,k=k)) 

# Plot the results 
plt.plot(x, y, '.', xp, pxp, '-') 
plt.ylim(0,1) 
plt.xlabel('x') 
plt.ylabel('y') 
plt.grid(True) 
plt.show()

この改訂されたコードを修正する方法を教えていただけますか?
注:データを再キャストすることにより、基本的に2d(x、y)シグモイドをz軸を中心に180度回転しました。また、1.000は実際には最大値ではありません。 x値。代わりに、1.000は、最大テスト条件でのさまざまなテスト参加者からの値の範囲の平均です。


以下の2番目の編集:

ありがとう、ubuntu。私はあなたのコードを注意深く読み、scipyのドキュメントでその側面を調べました。あなたの名前はscipyドキュメントのライターとしてポップアップしているようですので、次の質問に答えていただければ幸いです。

1.)leastsq()はresiduals()を呼び出しますか?これにより、入力yベクトルとsigmoid()関数によって返されるyベクトルの差が返されますか?もしそうなら、それは入力yベクトルとsigmoid()関数によって返されるyベクトルの長さの違いをどのように説明しますか?

2.)残余関数を介してその数学方程式にアクセスし、次に数学関数を呼び出す限り、任意の数学方程式に対してleastsq()を呼び出すことができるように見えます。これは本当ですか?

3.)また、p_guessにはpと同じ数の要素があることに気付きました。これは、p_guessの4つの要素が、x0、y0、c、およびkによって返される値とそれぞれ順番に対応していることを意味しますか?

4.)residuals()およびsigmoid()関数に引数として送信されるpは、leastsq()によって出力されるpと同じであり、leastsq()関数はそのpを内部で使用してから返しますか?

5.)pの要素の数がp_guessの要素の数と等しい限り、モデルとして使用されている方程式の複雑さに応じて、pとp_guessは任意の数の要素を持つことができますか?

28
MedicalMath

scipy.optimize.leastsq を使用する:

_import numpy as np
import matplotlib.pyplot as plt
import scipy.optimize

def sigmoid(p,x):
    x0,y0,c,k=p
    y = c / (1 + np.exp(-k*(x-x0))) + y0
    return y

def residuals(p,x,y):
    return y - sigmoid(p,x)

def resize(arr,lower=0.0,upper=1.0):
    arr=arr.copy()
    if lower>upper: lower,upper=upper,lower
    arr -= arr.min()
    arr *= (upper-lower)/arr.max()
    arr += lower
    return arr

# raw data
x = np.array([821,576,473,377,326],dtype='float')
y = np.array([255,235,208,166,157],dtype='float')

x=resize(-x,lower=0.3)
y=resize(y,lower=0.3)
print(x)
print(y)
p_guess=(np.median(x),np.median(y),1.0,1.0)
p, cov, infodict, mesg, ier = scipy.optimize.leastsq(
    residuals,p_guess,args=(x,y),full_output=1,warning=True)  

x0,y0,c,k=p
print('''\
x0 = {x0}
y0 = {y0}
c = {c}
k = {k}
'''.format(x0=x0,y0=y0,c=c,k=k))

xp = np.linspace(0, 1.1, 1500)
pxp=sigmoid(p,xp)

# Plot the results
plt.plot(x, y, '.', xp, pxp, '-')
plt.xlabel('x')
plt.ylabel('y',rotation='horizontal') 
plt.grid(True)
plt.show()
_

収量

alt text

シグモイドパラメータを使用

_x0 = 0.826964424481
y0 = 0.151506745435
c = 0.848564826467
k = -9.54442292022
_

新しいバージョンのscipy(0.9など)には、leastsqよりも使いやすい scipy.optimize.curve_fit 関数もあることに注意してください。 _curve_fit_を使用したシグモイドのフィッティングに関する関連する議論は ここ にあります。

編集:resize関数が追加され、生データを再スケーリングおよびシフトして、任意の境界ボックスに合わせることができるようになりました。


「あなたの名前はscipyドキュメントのライターとしてポップアップするようです」

免責事項:私はscipyドキュメントの作成者ではありません。私はただのユーザーであり、その初心者です。 leastsqについて私が知っていることの多くは、Travis Oliphantによって書かれた このチュートリアル を読むことから来ています。

1.)leastsq()はresiduals()を呼び出しますか?これは、入力yベクトルとsigmoid()関数によって返されるyベクトルの差を返しますか?

はい!丁度。

もしそうなら、それは入力yベクトルとsigmoid()関数によって返されるyベクトルの長さの違いをどのように説明しますか?

長さは同じです:

_In [138]: x
Out[138]: array([821, 576, 473, 377, 326])

In [139]: y
Out[139]: array([255, 235, 208, 166, 157])

In [140]: p=(600,200,100,0.01)

In [141]: sigmoid(p,x)
Out[141]: 
array([ 290.11439268,  244.02863507,  221.92572521,  209.7088641 ,
        206.06539033])
_

Numpyのすばらしい点の1つは、配列全体を操作する「ベクトル」方程式を記述できることです。

_y = c / (1 + np.exp(-k*(x-x0))) + y0
_

フロートで動作するように見えるかもしれませんが(実際は動作します)、xをnumpy配列にし、ck、_x0_、_y0_が浮動する場合、方程式はyxと同じ形状のnumpy配列として定義します。したがって、sigmoid(p,x)はnumpy配列を返します。これがどのように機能するかについてのより完全な説明が numpybook (numpyの深刻なユーザーのために読む必要があります)にあります。

2.)残余関数を介してその数学方程式にアクセスし、次に数学関数を呼び出す限り、任意の数学方程式に対してleastsq()を呼び出すことができるように見えます。これは本当ですか?

本当。 leastsqは、残差(差)の二乗和を最小化しようとします。パラメータ空間(pのすべての可能な値)を検索して、その二乗和を最小化するpを探します。 xに送信されるyresidualsは、生データ値です。それらは修正されています。それらは変わりません。 pが最小化しようとするのは、leastsqs(シグモイド関数のパラメーター)です。

3.)また、p_guessにはpと同じ数の要素があることに気付きました。これは、p_guessの4つの要素が、x0、y0、c、およびkによって返される値とそれぞれ順番に対応していることを意味しますか?

まさにそうです!ニュートン法と同様に、leastsqにはpの初期推定が必要です。 _p_guess_として提供します。あなたが見るとき

_scipy.optimize.leastsq(residuals,p_guess,args=(x,y))
_

最初のパスとして、leastsqアルゴリズム(実際にはLevenburg-Marquardtアルゴリズム)の一部として、leastsqがresiduals(p_guess,x,y)を呼び出すと考えることができます。間の視覚的な類似性に注意してください

_(residuals,p_guess,args=(x,y))
_

そして

_residuals(p_guess,x,y)
_

leastsqへの引数の順序と意味を覚えておくと役立つ場合があります。

residualsは、sigmoidのようにnumpy配列を返します。配列の値は2乗されてから、合計されます。これは打ち負かす数です。次に、leastsqresiduals(p_guess,x,y)を最小化する値のセットを探すときに、_p_guess_が変化します。

4.)residuals()およびsigmoid()関数に引数として送信されるpは、leastsq()によって出力されるpと同じであり、leastsq()関数はそのpを内部で使用してから返しますか?

まあ、正確ではありません。ご存知のように、leastsqresiduals(p,x,y)を最小化するp値を検索すると、_p_guess_は変化します。 pに送信されるleastsq(er、_p_guess_)は、pによって返されるleastsqと同じ形状です。あなたが推測者の地獄でない限り、明らかに値は異なるはずです:)

5.)pの要素の数がp_guessの要素の数と等しい限り、モデルとして使用されている方程式の複雑さに応じて、pとp_guessは任意の数の要素を持つことができますか?

はい。非常に多くのパラメーターについてleastsqのストレステストを行っていませんが、これはスリル満点の強力なツールです。

36
unutbu

すべての多項式は十分に大きいXと小さいXに対して無限大になりますが、シグモイド曲線は各方向である有限値に漸近的に近づくため、どの程度の多項式近似でも良い結果が得られるとは思いません。

私はPythonプログラマーではないので、numpyにもっと一般的なカーブフィッティングルーチンがあるかどうかはわかりません。自分でロールする必要がある場合は、おそらくこの記事 ロジスティック回帰 はあなたにいくつかのアイデアを与えるでしょう。

2
Jim Lewis

Pythonでのロジスティック回帰の場合、 scikits-learn は高性能のフィッティングコードを公開します。

http://scikit-learn.sourceforge.net/modules/linear_model.html#logistic-regression

1
Gael Varoquaux

上記の@unutbuで指摘されているように、scipyは、より複雑でない呼び出しを持つ scipy.optimize.curve_fit を提供します。同じプロセスがこれらの用語でどのように見えるかについての簡単なバージョンが必要な場合は、以下に最小限の例を示します。

def sigmoid(x, k, x0):

    return 1.0 / (1 + np.exp(-k * (x - x0)))

# Parameters of the true function
n_samples = 1000
true_x0 = 15
true_k = 1.5
sigma = 0.2

# Build the true function and add some noise
x = np.linspace(0, 30, num=n_samples)
y = sigmoid(x, k=true_k, x0=true_x0) 
y_with_noise = y + sigma * np.random.randn(n_samples)

# Sample the data from the real function (this will be your data)
some_points = np.random.choice(1000, size=30)  # take 30 data points
xdata = x[some_points]
ydata = y_with_noise[some_points]

# Fit the curve
popt, pcov = curve_fit(return_sigmoid, xdata, ydata)
estimated_k, estimated_x0 = popt

# Plot the fitted curve
y_fitted = sigmoid(x, k=estimated_k, x0=estimated_x0)

# Plot everything for illustration
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y_fitted, '--', label='fitted')
ax.plot(x, y, '-', label='true')
ax.plot(xdata, ydata, 'o', label='samples')

ax.legend()

この結果を次の図に示します。

enter image description here

1
Ramon Martinez