本文是对Andrew Ng的Machine Learning课程里编程作业的一个numpy实现。
Hypothesis 定义为 h θ ( X ) = X θ h_\theta (X) = X\theta hθ(X)=Xθ
一元线性回归的例子: h θ ( X ) = X θ = θ 0 + θ 1 x 1 h_\theta (X) = X\theta = \theta_0 + \theta_1x_1 hθ(X)=Xθ=θ0+θ1x1, 其中 θ = ( θ 0 , θ 1 ) T , X = ( 1 , x 1 ) \theta = (\theta_0, \theta_1)^T, X = (1, x_1) θ=(θ0,θ1)T,X=(1,x1)
J ( θ ) = 1 2 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) 2 J(θ)= \frac{1}{2m}\sum_{i=1}^{m} (h_\theta(x^{(i)}) -y^{(i)})^2 J(θ)=2m1∑i=1m(hθ(x(i))−y(i))2 ,其中m为训练数据的个数。
因为使用Numpy实现,我们可以使用更为效率的向量化表示方法: J ( θ ) = 1 2 m ( X θ − y ) T ( X θ − y ) J(θ)= \frac{1}{2m} (X\theta - y)^T(X\theta - y) J(θ)=2m1(Xθ−y)T(Xθ−y),其中X的第i行就是表示第i个数据,而y则是对应的真实的值。
def compute_cost(X, y, theta):
Hx = np.dot(X, theta)
return np.dot((Hx - y).transpose(), Hx - y) / (2 * X.shape[0])
使用梯度下降算法更新theta的值,以最小化损失函数,在每一次迭代中都同步更新theta向量里的每一个元素,公式如下:
def gradient_descent(X, y, theta, learning_rate, iteration):
cost_his = np.zeros((iteration, 1))
cost_his[0] = compute_cost(X, y, theta)
for i in range(1,iteration):
Hx = np.dot(X, theta)
theta = theta - (learning_rate / X.shape[0]) * (np.dot((Hx - y).transpose(), X)).transpose()
cost_his[i] = compute_cost(X, y, theta)
return theta, cost_his
其中 alpha 是 learning rate, 即学习率,控制每次梯度下降的大小, 若learning rate太小,则时间消耗太大,若learning rate太大,则梯度下降很有可能没有效果,损失函数可能越来越大,适得其反。
当然,我们在每一次迭代中也使用向量化的方法,公式可表示为: θ : = θ − α 1 m ( ( X θ − y ) T X ) T \theta := \theta - \alpha\frac{1}{m} ((X\theta - y)^TX)^T θ:=θ−αm1((Xθ−y)TX)T
因为每一个数据样本的特征数值的尺度可能会有很大的差别,例如某个特征数值的范围在[1, 10], 而另一个特征数值的范围在[10000, 100000],若不进行标准化,使用梯度下降时的效果就不佳,若是特征数值都相差无几,可用可不用。
通常用于 特征标准化的途径有两种, 一种叫做 min max normalization, 他会将所有特征数据按比例缩放到0-1的这个取值区间。 有时也可以是-1到1的区间. 还有一种叫做 standard deviation normalization, 他会将所有特征数据缩放成 平均值为0, 方差为1。
在这里我使用standard deviation normalization: X n o r m = X − X m e a n X s t d X_{norm} = \frac{X-X_{mean}}{X_{std}} Xnorm=XstdX−Xmean
def feature_normalize(X):
mean = np.mean(X, axis=0, keepdims=True)
std = np.std(X, axis=0, keepdims=True)
X_norm = (X - mean) / std
return X_norm
在课上Ng一笔带过了Normal Equation, θ = ( X T X ) − 1 X T y \theta = (X^TX)^{-1}X^Ty θ=(XTX)−1XTy。
这是线性回归的closed-form solution。使用这个解法,不需要特征标准化,而且也无需迭代,一次就搞定。但是X仍然需要加一列全为一的列向量作为截距。
def normal_equation(X,y):
a = np.dot(X.transpose(), X)
b = np.dot(X.transpose(), y)
return np.dot(np.linalg.inv(a), b)