最近手写了linear regression,有以下几点收获:
- 做batch gradient descent时,注意每一轮迭代要使用同一个error同时更新所有参数。
- 归一化的时候,要注意记录相应的均值和方差,后续对新样本做预测时也需要使用这两个参数,对特征做归一化。
- 为何要做特征归一化?不做归一化,可能会降低模型的训练效率。
- 不做归一化,模型也可以收敛,但需要选择合适的学习率lambda。
相关代码如下,数据取自吴恩达机器学习的课后作业(GitHub上可以找到)。
## 数据探索
import numpy as np
import matplotlib.pyplot as plt
# # 载入数据
# data = np.loadtxt('./ex1data1.txt', delimiter=',')
# # 区分特征X和标签y
# X,y = data[:,:1], data[:,1]
# 载入数据
data = np.loadtxt('./ex1data2.txt', delimiter=',')
# 区分特征X和标签y
X,y = data[:,:2], data[:,2]
# 数据维度
print data.shape, X.shape, y.shape
# 增加全1列,对应表达式的常数项
X=np.c_[np.ones(X.shape[0]),X]
print X.shape
# 绘图
plt.scatter(X[:,1],y)
plt.show()
## 计算模型函数和代价函数
import numpy as np
# 定义模型函数
def h(Theta, X):
# Theta: 参数向量
# x: 特征向量
return np.dot(X,Theta.T)
# 定义代价函数,计算m个样本的损失
def J(Theta,X,y):
# m表示样本数量
m=X.shape[0]
return 1.0/(2*m)*sum((np.dot(X,Theta.T)-y)**2)
# 初始化参数向量,参数向量的长度等于特征列的数量
Theta=np.zeros(X.shape[1])
print np.dot(X,Theta.T).shape, J(Theta,X,y)
## 模型训练:参数迭代更新(归一化)
# 梯度下降:同时对每一个参数进行迭代
def model_fit(Theta,X,y,alpha,iterations):
# 代价函数的迭代过程记录
cost=np.zeros(iterations)
# 样本数量
m=X.shape[0]
# 迭代次数
for i in range(iterations):
# 必须要先计算error:对所有参数进行更新时,使用相同的error值
error=np.dot(X,Theta.T)-y
# batch gradient descent:对参数进行更新
for j in range(len(Theta)):
Theta[j]=Theta[j]-1.0/m*alpha*np.dot(error,X[:,j])
cost[i]=J(Theta,X,y)
return Theta,cost
# 归一化时:剔除全1列
Xless=X[:,1:]
# 存储归一化参数
X_mean=np.mean(Xless, axis=0)
X_std=np.std(Xless, axis=0)
# 归一化
Xless_std=(Xless-X_mean)/X_std
X_std=np.c_[np.ones(X.shape[0]),Xless_std]
# 定义迭代参数,如果alpha的数量级选择不正确,有可能会无法收敛
alpha,iterations=0.1,50
# 初始化参数
init_theta=np.zeros(X_std.shape[1])
# 模型训练
theta,cost=model_fit(init_theta,X_std,y,alpha,iterations)
print cost,theta
# 绘图:原始数据
plt.scatter(X_std[:,1],y)
plt.plot(X_std[:,1],np.dot(X_std,theta),color='red')
plt.show()
# 绘图:收敛速度
plt.plot([i for i in range(len(cost))],cost,marker='x')
plt.show()