Andrew Ng の「機械学習」 - 線形回帰コードの実装


データセットとソースファイルは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+×
wwwbbbはパラメータです。計算を容易にするために、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バツy2
不適切なデータ範囲によって引き起こされる過剰または小規模な損失を回避するため (たとえば、データ値が大きすぎる場合、損失は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 _jJ ( 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 _jJ ( w ,b )=wjあるメートル1i = 1メートル( hw bバツy( i ) )θ \theta
を使用できます。θ wwを含む統一識別パラメータwbbb

つまり、jjthjパラメータθ j \theta_jj次のことを決定します。
θ 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 _jJ ( θ ;バツ=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\sim101×1×_1バツ1範囲は1 0 3 〜 1 0 4 10^3\sim10^41 031 0勾配に応じてθ 0 〜 θ 2 \theta_0\sim\theta_2を同時に最適化します02、すべてが同じサイズで変化するように、入力サンプルが同じ場合、明らかにθ 1 \theta_11変化はθ 2 \theta_2より大きくなります2より大きな生産量につながる変化。これは、モデルペアθ 1 \theta_1としても理解できます。1より敏感に。次の損失等値線図に示すように、θ 1 \theta_11小さな変化が損失に大きな変化をもたらす可能性があります。この場合、パラメータの最適化はより困難になります。
ここに画像の説明を挿入します
この問題を解決する 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 )

  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バツ

  2. 量 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 _jJ ( θ ;バツ=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 _0J ( θ ;バツ=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 ( xy( i ) )x0( 1 )i = 1( h ( xy( 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. 実験結果

単変量回帰

データセット視覚化
ここに画像の説明を挿入します
トレーニングセットとテストセット部門
blog.csdnimg.cn/c92b41d0e88f4afab794315c992525a0.png)

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 ライブラリ関数のレベルにはまだ達していません。

おすすめ

転載: blog.csdn.net/d33332/article/details/128455218
おすすめ