python机器学习——自适应线性神经元

上篇博客咱们说了感知器,这篇博客主要记录自适应线性神经元的实现算法及一些其余的训练细节,自适应线性神经元(简称为Adaline)由Bernard Widrow和他的博士生Tedd Hoff提出,对感知器算法进行了改进。python

固然Adaline对输入向量x的处理和感知器是同样的,都是使用一个权重向量w与x线性组合后获得z,再使用函数将z压缩到二元输入(1/-1),区别在于Adaline使用梯度降低法来更新w。算法

由于咱们的目的是准确分类,那么咱们须要来衡量分类效果的好坏,在这里咱们介绍目标函数: $$ J(w) = \frac12 \sum_i^n(y^i - \phi(z^i))^2 $$ 它也能够叫作损失函数,经过上式咱们能够大体理解为何叫作损失函数,此函数能够计算出全部训练样本的真实值和预测值之间的偏差平方和(Sum of Squared Errors,简称SSE),式子前面的那个1/2是为了以后求导方便添加的,没有其余意义。app

有了损失函数,因而咱们的目的更具体一点,就是为了选择合适的w,使损失函数取得最小值,损失函数越小,就意味着错误分类的状况越少,算法的分类效果也就越好。而由于Adaline的损失函数是一个凸函数,因此咱们可使用梯度降低来找到使损失函数取值最小的权重向量w,咱们能够想象为一个小球滚下山:函数

刚开始的w也许会获得一个很大的损失函数,可是因为损失函数J是w的函数,而且也是一个凸函数,它存在一个最小值,学过微积分的朋友应该知道,要找到一个函数的最值,通常的方法经过求导并使导数为零,解出的那个x就是最值,在这里的梯度降低也就是求导,但因为w是一个权重向量,是多维的,因此须要损失函数对w求偏导,获得w中每一个份量的偏导数,而后再更新整个w,具体的推导过程以下: $$ 注意:w为向量,w_j为向量w中的某一份量\w = w + \Delta w\\Delta w = -\eta\Delta J(w)\\frac{\partial J}{\partial w_j} = \frac{\partial }{\partial w_j} \frac 12 \sum_i(y^i-\phi(z^i))^2 \= \frac 12 \frac{\partial }{\partial w_j} \sum_i(y^i-\phi(z^i))^2 \= \frac 12 \sum_i2(y^i-\phi(z^i)) \frac{\partial }{\partial w_j}(y^i-\phi(z^i))\= \sum_i(y^i-\phi(z^i))\frac{\partial }{\partial w_j}(y^i-\sum_i(w_j^ix_j^i))\=\sum_i(y^i-\phi(z^i))(-x_j^i)\=-\sum_i(y^i-\phi(z^i))x_j^i\因此\Delta w_j = -\eta\frac{\partial J}{\partial w_j}=\eta\sum_i(y^i-\phi(z^i))x_j^i $$ 一点要注意因此的权重向量w中的份量是同时更新的,并且每次更新都用到了全部的训练样本,因此梯度降低法也被称为批量梯度降低(batch gradient descent)学习

接下来咱们具体来实现自适应线性神经元,因为和感知机的学习规则很类似,因此直接在感知器的基础上进行修改获得,其中须要修改fit方法,由于在这里咱们要使用梯度降低算法。.net

class AdalineGD(object):
    """ADAptive LInear NEuron classifier.

    Parameters
    ----------
    eta:float
        Learning rate(between 0.0 and 1.0
    n_iter:int
        Passes over the training dataset.

    Attributes
    ----------
    w_:1d-array
        weights after fitting.
    errors_:list
        Number of miscalssifications in every epoch.

    """

    def __init__(self, eta=0.01, n_iter=10):
        self.eta = eta
        self.n_iter = n_iter

    def fit(self, X, y):
        """Fit training data.

        :param X:{array-like}, shape=[n_samples, n_features]
        Training vectors,
        :param y: array-like, shape=[n_samples]
        Target values.
        :return:
        self:object

        """

        self.w_ = np.zeros(1 + X.shape[1]) # Add w_0
        self.cost_ = []

        for i in range(self.n_iter):
            output = self.net_input(X)
            errors = (y - output)
            self.w_[1:] += self.eta * X.T.dot(errors)
            self.w_[0] += self.eta * errors.sum()
            cost = (errors ** 2).sum() / 2.0
            self.cost_.append(cost)
        return self

    def net_input(self, X):
        """Calculate net input"""
        return np.dot(X, self.w_[1:]) + self.w_[0]
    
    def activation(self, X):
        """Computer linear activation"""
        return self.net_input(X)
    
    def predict(self, X):
        """Return class label after unit step"""
        return np.where(self.activation(X) >= 0.0, 1, -1)

分别使用不一样的学习率(0.01和0.0001)训练,观察神经元学习过程。其中学习率、迭代次数咱们称他们为超参数(hyperparameters),咱们能够手动设置,超参数设置的是否合适对于整个训练过程都很重要。code

fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(8,4))
ada1 = AdalineGD(n_iter=10, eta=0.01).fit(X, y)
ax[0].plot(range(1, len(ada1.cost_) + 1), np.log10(ada1.cost_), marker='o')
ax[0].set_xlabel('Epochs')
ax[0].set_ylabel('log(Sum-squared-error)')
ax[0].set_title('Adaline - Learning rate 0.01')
ada2 = AdalineGD(n_iter=10, eta=0.0001).fit(X, y)
ax[1].plot(range(1, len(ada2.cost_) + 1), ada2.cost_, marker='o')
ax[1].set_xlabel('Epochs')
ax[1].set_ylabel('Sum-squared-error')
ax[1].set_title('Adaline - Learning rate 0.0001')
plt.show()

能够看出,左图中学习率为0.01,随着迭代次数的增长,偏差在增长,说明学习率设置的不合适,产生了很大的危害,而右图学习率为0.0001,随着迭代次数的增长,偏差在减小,可是减小的过于缓慢,算法收敛的很慢,训练的效率过低,因此咱们能够看出过大或太小的学习率都是不合适的。blog

由右图能够看出,若是学习率过大,就会致使每次梯度降低时都跳过了对应最小值的权重向量w,使得算法没法收敛。ci

接下来咱们介绍一种数据预处理方法,在训练前将特征进行某种缩放操做,这里咱们称为特征标准化,可使全部特征数据缩放成平均值为0,方差为1,加快模型的训练速度,并且能够避免模型学习的很扭曲。get

具体公式以下: $$ x_j^, = \frac {x_j-\mu_j}{\sigma_j} $$ 具体实现以下:

X_std = np.copy(X)
X_std[:, 0] = (X[:,0] - X[:,0].mean()) / X[:,0].std()
X_std[:, 1] = (X[:,1] - X[:,1].mean()) / X[:,1].std()

数据已经预处理结束,接下来咱们开始训练模型

ada = AdalineGD(n_iter=15, eta=0.01)
ada.fit(X_std, y)
plot_decision_region(X_std, y, classifier=ada)
plt.title('Adaline - Gradient Descent')
plt.xlabel('sepal length [standardized]')
plt.ylabel('petal length [standardized]')
plt.legend(loc='upper left')
plt.show()

plt.plot(range(1, len(ada.cost_) + 1), ada.cost_, marker='o')
plt.xlabel('Epoches')
plt.ylabel('Sum-squared_error')
plt.show()

从上图看出,随着迭代次数的增长,偏差逐渐下降,虽然学习率为0.01,在进行标准化以前,算法并不能收敛,但通过标准化后,算法最终收敛。

相关文章
相关标签/搜索