線形回帰
データセットとソースファイルはGithubプロジェクトで入手できます
アドレス: https://github.com/Raymond-Yang-2001/AndrewNg-Machine-Learing-宿題
1. 単変量線形回帰
単変量線形回帰では、1 次元の方程式が求められ、直線が近似されます。
単変量線形回帰式
hw , b ( x ) = b + wx h_{w,b}(x)=b+wxhw 、 b(バツ)=b+w×
wwwとbbbはパラメータです。計算を容易にするために、xxx に xを加えたもの0 = 1 x_0=1バツ0=1
hw , b ( x ) = bx 0 + wx 1 h_{w,b}(x)=bx_{0}+wx_{1}hw 、 b(バツ)=bx _0+w ×1
損失関数
J ( w , b ) = 1 2 m ∑ i = 1 m ( hw , b ( x ( i ) ) − y ( i ) ) 2 J(w,b)=\frac{1}{2m}\sum_{ i=1}^{m}(h_{w,b}(x^{(i)})-y^{(i)})^{2}J ( w ,b )=2m_ _1i = 1∑メートル( hw 、 b(バツ(私))−y(私))2
不適切なデータ範囲によって引き起こされる過剰または小規模な損失を回避するため (たとえば、データ値が大きすぎる場合、損失は1 0 5 10^51 05または1 0 6 10^61 06、この桁の大きさは直感的な分析には適していません) 損失を評価するときは、 hw, b (x (i)) h_{w,b}(x^{(i)}) を行うhw 、 b(バツ(i))和 y ( i ) y^{(i)} y( i )まず、損失値が評価可能な範囲内になるように規格化します。ただし、勾配降下を行う場合、これは行われません
最適化アルゴリズム - バッチ勾配降下法 (BGD)
wj = wj − α ∂ ∂ wj J ( w , b ) = wj − α 1 m ∑ i = 1 m ( hw , b ( x ( i ) ) − y ( i ) ) x ( i ) w_j=w_{j }-\alpha\frac{\partial}{\partial{w_j}}{J(w,b)}=w_{j}-\alpha \frac{1}{m}\sum_{i=1}^{ m}{(h_{w,b}(x^{(i)})-y^{(i)})x^{(i)}}wj=wj−ある∂w _j∂J ( w ,b )=wj−あるメートル1i = 1∑メートル( hw 、 b(バツ(私))−y( i ) )x( i )
bj = bj − α ∂ ∂ bj J ( w , b ) = wj − α 1 m ∑ i = 1 m ( hw , b ( x ( i ) ) − y ( i ) ) b_j=b_{j} -\alpha\frac{\partial}{\partial{b_j}}{J(w,b)}=w_{j}-\alpha \frac{1}{m}\sum_{i=1}^{m }{(h_{w,b}(x^{(i)})-y^{(i)})}bj=bj−ある∂b _j∂J ( w ,b )=wj−あるメートル1i = 1∑メートル( hw 、 b(バツ(私))−y( i ) )θ \theta
を使用できます。θ wwを含む統一識別パラメータwとbbb。
つまり、jjthjパラメータθ j \theta_j私j次のことを決定します。
θ j = θ j − α ∂ ∂ wj J ( θ ; x ) = wj − α 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x ( i ) \theta_{j}=\theta_{j}-\alpha\frac{\partial}{\partial{w_j}}{J(\theta;\mathbf{x})}=w_{j}-\alpha \frac {1}{m}\sum_{i=1^{m}{(h_{\theta}(x^{(i)})-y^{(i)})x^{(i) }}私j=私j−ある∂w _j∂J ( θ ;バツ)=wj−あるメートル1i = 1∑メートル( h私(バツ(私))−y( i ) )x( i )
ここで、α \alphaαは学習率です。
2. 多変数線形回帰
多変数線形回帰では、複数の変数と予測値の間の関係を見つけようとします。たとえば、家の大きさ、家の寝室の数、家の価格の関係などです。
特徴のスケーリング (正規化)
サンプルのさまざまな特徴間の数値の差が大きすぎる場合、勾配ベースの最適化手法ではいくつかの問題が発生します。たとえば、次の回帰式があります。
h θ ( x ) = θ 0 + θ 1 x 1 + θ 2 x 2 h_{\theta}(x)=\theta_{0}+\theta_{1}x_{ 1}+\ シータ_{2}x_{2}h私(バツ)=私0+私1バツ1+私2バツ2
x 2 x_{2}と仮定しますバツ2範囲は0〜1 0\sim10〜1、×1×_1バツ1範囲は1 0 3 〜 1 0 4 10^3\sim10^41 03〜1 04.勾配に応じてθ 0 〜 θ 2 \theta_0\sim\theta_2を同時に最適化します私0〜私2、すべてが同じサイズで変化するように、入力サンプルが同じ場合、明らかにθ 1 \theta_1私1変化はθ 2 \theta_2より大きくなります私2より大きな生産量につながる変化。これは、モデルペアθ 1 \theta_1としても理解できます。私1より敏感に。次の損失等値線図に示すように、θ 1 \theta_1私1小さな変化が損失に大きな変化をもたらす可能性があります。この場合、パラメータの最適化はより困難になります。
この問題を解決する 1 つの方法は、2 つのフィーチャを同じ範囲にスケーリングするフィーチャ スケーリングです。たとえば、Z スコア正規化は次のように実行できます。
xnew = x − μ σ x_{new} = \frac{x-\mu}{\sigma}バツ新しい_ _=pバツ−メートル
その中で、μ \muμ はデータセットの平均です、σ \sigmaσは標準偏差で、新しいデータの分布は平均 0、標準偏差 1 の分布になります。
データ正規化後のパラメータ損失図は次のとおりです。
パラメータの逆スケーリング
データがスケーリングされるため、最終パラメータもそれに応じてスケーリングされます。具体的な関係は次のとおりです。
θ 0 + θ 1 〜 d + 1 x 1 〜 d + 1 − μ x σ x = y − μ y σ y \theta_{0}+\theta_{1\sim d+1} \frac {x_{1\sim d+1}-\mu_{x}}{\sigma_{x}}=\frac{y-\mu_{y}}{\sigma_{y}}私0+私1 〜d + 1p×バツ1 〜d + 1−メートル×=pはいy−メートルはい
ここではyyについて話していますyも標準化されていますが、実際にはこれを行う必要はなく、パフォーマンスには影響しません。ただし、y を正規化するとパラメータが小さくなり、0 に初期化されたパラメータではより速く収束することができます。
標準化中yyこの場合、パラメータの逆スケーリング式は次のようになります。
θ 1 〜 d + 1 new = θ 1 〜 d + 1 σ x σ y \theta_{1\sim d+1}^{new}=\frac{\ theta_{1\sim d+1}}{\sigma_{x}}\sigma_{y}私1 〜d + 1新しい_ _=p×私1 〜d + 1pはい
計算式:
θ 0 σ y + θ 1 〜 d + 1 new ( x 1 〜 d + 1 − μ x ) = y − μ y \theta_{0}\sigma_{y}+\theta_{1\sim d+1 }^{new}(x_{1\sim d+1}-\mu_{x})=y-\mu_{y}私0pはい+私1 〜d + 1新しい_ _(バツ1 〜d + 1−メートル×)=y−メートルはい
θ 0 new = θ 0 σ y + μ y − θ 1 〜 d + 1 new μ x \theta_{0}^{new}=\theta_{0}\sigma_{y}+\mu_{y}-\theta_ {1\sim+1}^{new}\mu_{x}私0新しい_ _=私0pはい+メートルはい−私1 〜d + 1新しい_ _メートル×
このうち、ベクトル化演算中は、θ 1 〜 d + 1 new \theta_{1\sim d+1}^{new}私1 〜d + 1新しい_ _和μ x \mu_{x}メートル×これらはすべて (1,d) のベクトルであり、乗算にはベクトル内積を使用する必要があります。
3. 線形回帰アルゴリズムのコード実装
ベクトル実装
データを\boldsymbol{x}とします。xの次元は(n, d) (n,d)です。( n 、d )、ここで、n はサンプルの数、d はサンプル フィーチャの次元です。計算の便宜上、すべての値が 1 である追加の特徴次元をサンプルに追加し、その次元が( n , d + 1 ) (n, d+1)( n 、d+1 )
- 予測
パラメータをθ \boldsymbol{\theta}としますθの次元は (1, d+1) なので、x θ ⊤ \boldsymbol{x\theta^{\top}}× θ⊤または( θ x ⊤ ) ⊤ \boldsymbol{(\theta x^{\top})^{\top}}( θ x⊤ )⊤次元は(n, 1) (n,1)として取得できます。( n 、1 )予測結果h θ ( x ) h_{\boldsymbol{\theta}}(\boldsymbol{x})h私(バツ)。
量 j を量で割ってみましょう
: θ j = θ j − α ∂ ∂ wj J ( θ ; x ) = wj − α 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x ( i ) \theta_{j}=\theta_{j}-\alpha\frac{\partial}{\partial{w_j}}{J(\theta;\mathbf{x})}=w_ {j }-\alpha\frac{1}{m}\sum_{i=1}^{m}{(h_{\theta}(x^{(i)})-y^{(i)}) x^ {(私)}}私j=私j−ある∂w _j∂J ( θ ;バツ)=wj−あるメートル1i = 1∑メートル( h私(バツ(私))−y( i ) )x( i )
実際、ここではθ 0 \theta_{0}私0バイアスとして、その勾配は次のようになります:
∂ ∂ w 0 J ( θ ; x ) = w 0 − α 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) \frac{\部分的}{\partial{w_0}}{J(\theta;\mathbf{x})}=w_{0}-\alpha \frac{1}{m}\sum_{i=1}^{m}{ (h_{\theta}(x^{(i)})-y^{(i)})}∂w _0∂J ( θ ;バツ)=w0−あるメートル1i = 1∑メートル( h私(バツ(私))−y( i ) )x 0 x_0でデータを補足するためです。バツ0, そのため、他のパラメータと同様に上記の式を使用して計算できます。エラーエラー
をさせます誤差行列β \boldsymbol{\beta}β=h θ ( x ) − x h_{\boldsymbol{\theta}}(\boldsymbol{x})-\boldsymbol{x}h私(バツ)−x、次元( n , 1 ) (n,1)( n 、1 )の場合、x ⊤ β / n \boldsymbol{x^{\top}\beta} /nバツ⊤ β/nは次元(d + 1, 1) (d+1,1)( d+1 、x ⊤ β = [ x ( 1 ) x ( 2 ) ⋯ ] × [ h ( x ( 1 ) ) − y ( 1 ) h ( x ( 2 ) ) − y ( 2 ) ⋮ ] =
[ ∑ i = 1 n ( h ( x ( i ) ) − y ( i ) ) x 0 ( 1 ) ∑ i = 1 n ( h ( x ( i ) ) − y ( i ) ) x 1 ( 1 ) ⋮ ] \boldsymbol{x^{\top}\beta}= \left[ \begin{行列} x^{(1)}& x^{(2)} &\cdots \end{行列} \right] \times \left[ \begin{行列} h(x^{(1)})-y^{(1)}\\ h(x^{(2)})-y^{(2)}\\ \vdots \end{行列} \right] =\left[ \begin{行列} \sum_{i=1}^{n}{(h(x^{(i)})-y^{(i)})x_ {0}^{(1)}}\\ \sum_{i=1}^{n}{(h(x^{(i)})-y^{(i)})x_{1}^{ (1)}}\\ \vdots \end{行列} \right]バツ⊤ β=[バツ( 1 )バツ( 2 )⋯]× h ( x( 1 ) )−y( 1 )h ( x( 2 ) )−y( 2 )⋮ = ∑i = 1ん( h ( x(私))−y( i ) )x0( 1 )∑i = 1ん( h ( x(私))−y( i ) )x1( 1 )⋮
x ⊤ β / n \boldsymbol{x^{\top}\beta} /nバツ⊤ β/nの各要素は、
Pythonコード
import numpy as np
def square_loss(pred, target):
"""
计算平方误差
:param pred: 预测
:param target: ground truth
:return: 损失序列
"""
return np.sum(np.power((pred - target), 2))
def compute_loss(pred, target):
"""
计算归一化平均损失
:param pred: 预测
:param target: ground truth
:return: 损失
"""
pred = (pred - pred.mean(axis=0)) / pred.std(axis=0)
target = (pred - target.mean(axis=0)) / target.std(axis=0)
loss = square_loss(pred, target)
return np.sum(loss) / (2 * pred.shape[0])
class LinearRegression:
"""
线性回归类
"""
def __init__(self, x, y, val_x, val_y, epoch=100, lr=0.1):
"""
初始化
:param x: 样本, (sample_number, dimension)
:param y: 标签, (sample_numer, 1)
:param epoch: 训练迭代次数
:param lr: 学习率
"""
self.theta = None
self.loss = []
self.val_loss = []
self.n = x.shape[0]
self.d = x.shape[1]
self.epoch = epoch
self.lr = lr
t = np.ones(shape=(self.n, 1))
self.x_std = x.std(axis=0)
self.x_mean = x.mean(axis=0)
self.y_mean = y.mean(axis=0)
self.y_std = y.std(axis=0)
x_norm = (x - self.x_mean) / self.x_std
y_norm = (y - self.y_mean) / self.y_std
self.y = y_norm
self.x = np.concatenate((t, x_norm), axis=1)
self.val_x = val_x
self.val_y = val_y
def init_theta(self):
"""
初始化参数
:return: theta (1, d+1)
"""
self.theta = np.zeros(shape=(1, self.d + 1))
def validation(self, x, y):
x = (x - x.mean(axis=0)) / x.std(axis=0)
y = (y - y.mean(axis=0)) / y.std(axis=0)
outputs = self.predict(x)
curr_loss = square_loss(outputs, y) / (2 * y.shape[0])
return curr_loss
def gradient_decent(self, pred):
"""
实现梯度下降求解
"""
# error (n,1)
error = pred - self.y
# gradient (d+1, 1)
gradient = np.matmul(self.x.T, error)
# gradient (1,d+1)
gradient = gradient.T / pred.shape[0]
# update parameters
self.theta = self.theta - (self.lr / self.n) * gradient
def train(self):
"""
训练线性回归
:return: 参数矩阵theta (1,d+1); 损失序列 loss
"""
self.init_theta()
for i in range(self.epoch):
# pred (1,n); theta (1,d+1); self.x.T (d+1, n)
pred = np.matmul(self.theta, self.x.T)
# pred (n,1)
pred = pred.T
curr_loss = square_loss(pred, self.y) / (2 * self.n)
val_loss = self.validation(self.val_x, self.val_y)
self.gradient_decent(pred)
self.val_loss.append(val_loss)
self.loss.append(curr_loss)
print("Epoch: {}/{}\tTrain Loss: {:.4f}\tVal loss: {:.4f}".format(i + 1, self.epoch, curr_loss, val_loss))
# un_scaling parameters
self.theta[0, 1:] = self.theta[0, 1:] / self.x_std.T * self.y_std[0]
self.theta[0, 0] = self.theta[0, 0] * self.y_std[0] + self.y_mean[0] - np.dot(self.theta[0, 1:], self.x_mean)
return self.theta, self.loss, self.val_loss
def predict(self, x):
"""
回归预测
:param x: 输入样本 (n,d)
:return: 预测结果 (n,1)
"""
# (d,1)
t = np.ones(shape=(x.shape[0], 1))
x = np.concatenate((t, x), axis=1)
pred = np.matmul(self.theta, x.T)
return pred.T
4. 実験結果
単変量回帰
データセット視覚化
トレーニングセットとテストセット部門
from LinearRegression import LinearRegression
epochs = 200
alpha = 1
linear_reg = LinearRegression(x=train_x_ex,y=train_y_ex,val_x=val_x_ex, val_y=val_y_ex, lr=alpha,epoch=epochs)
start_time = time.time()
theta,loss, val_loss = linear_reg.train()
end_time = time.time()
Train Time: 0.0309s
Val Loss: 6.7951
トレーニングプロセスの可視化
とsk-learn比較予測曲線
多変数回帰
データの視覚化とトレーニング セットおよび検証セット
from LinearRegression import LinearRegression
alpha = 0.1
epochs = 1000
multi_lr = LinearRegression(train_x,train_y_ex,val_x=val_x,val_y=val_y_ex, epoch=epochs,lr=alpha)
start_time = time.time()
theta, loss, val_loss = multi_lr.train()
end_time = time.time()
Train Time: 0.1209s
Val Loss: 4.187(采用归一化后数据计算损失)
トレーニングプロセスの可視化
予測平面 (sk-learn との比較)。
青はこのアルゴリズムの予測平面、灰色は sk-learn の予測平面です。
実験概要
線形回帰アルゴリズムを実装してパフォーマンスを向上させるには、学習率または反復回数を調整してパフォーマンスを向上させることができます。ループの代わりに行列演算を使用するため、学習時間は大幅に短縮されますが、sk-learn ライブラリ関数のレベルにはまだ達していません。