单变量线性回归
说到机器学习的话,很多人首先会想到的是单变量的线性回归,好像大家高中的时候都学过。很多人不理解,这种线性回归的东西跟机器学习有什么关系。
其实单变量的话形式很简单:
但是让这种函数充分拟合大致有线性关系的离散点,怎么做到呢?这就需要一个损失函数了:
其实就是回归函数值与真实的y值的欧式距离求和。想要拟合效果最好,就要使得损失函数最小化。怎么最小化呢?
我们很容易看得出损失函数是个凸函数,二阶导数大于零。也就说有最优解。怎么求最优解呢?另两个变量的偏导等于0,即可。
令两式都为0.解得:
这个就是高中我们学过最小二乘法解线性回归的参数了!
行吧,先拿单变量练一练手。
数据集是关于年龄与身高的,来源:点击打开链接
把两个文件与代码放在同一个文件下面。然后就可以打码了。
import numpy as np
import matplotlib.pyplot as plt
def linear_regression_one_var(x, y):
# y=bx+a
num = len(x)
b = (np.sum(x*y)-num*np.mean(x)*np.mean(y))/(np.sum(x*x)-num*np.mean(x)**2)
a = np.mean(y)-b*np.mean(x)
return np.array([b, a])
x = np.loadtxt('ex2x.dat')
y = np.loadtxt('ex2y.dat')
b, a = linear_regression_one_var(x, y)
print(b,a)
fx = b*x+a
plt.plot(x, fx)
plt.plot(x, y, 'o')
plt.xlabel('age')
plt.ylabel('height')
plt.show()
输出:
0.0638811658258342 0.7501625370012386
等等,上面写的还停留在标量的阶段,实在是很不简洁,要是算多变量就很麻烦了。
这该怎么办呢?向量化,矩阵化!
重新定义参数,W是参数的矩阵,本质上是一个列向量,w0其实是常量的参数,不是变量的参数,为了运算的方便,纳入到参数矩阵中。X是变量的矩阵,上标代表是哪一个样本,每一列都是一个样本的特征变量,这里是单变量线性回归嘛,所以每列第一个都是1,代表那个常量,每列第二个就是该样本的特征变量了,因为是单变量,所以就不添下标了。Y的话就不用说了,本质上是一个列向量。
好了,那个回归函数就很容易写出来了。
我写的这个形式和别人的可能不太一样。
然后定义损失函数:
m是样本的个数,这个实践例子中,m=50,为什么是2m呢?为了求导后的形式美观,待会你看就知道了。
改变W矩阵,使得损失函数最小化!很简单,令
其实这个0是零矩阵,这是矩阵求导!矩阵求导细节,知乎上有个很好的讲解:矩阵求导术
还是写下求导的具体过程:
1、处理J(W):
2、套迹,其实我们知道J(W)是标量,所以可以套个迹,方便后面的微分
3、全微分
4、求导数
终于把导数求出来了。如果直接令导数等于0,解W是可以的。
这种求法的弊端在于矩阵的逆不存在时(也就是有多个最优解时),就不能用这种方法算了。
更推荐一种方法是,梯度下降法。原理的话,网上大把
具体形式:
alpha是步长。行吧最后上代码:
import numpy as np
def gradient_descent(x, y, w, col, alpha=0.06,times=3000,eps=1e-9): # 调参有点难受啊
for i in range(times):
y_predit_T = x.T * w
dJ_dw = 1 / col * x * (y_predit_T - y.T)
w -= alpha * dJ_dw # 同一个w矩阵
if np.sum(np.abs(dJ_dw))<eps:
print('第{}次迭代后的结果'.format(i))
break
x = np.loadtxt('ex2x.dat')
y = np.loadtxt('ex2y.dat')
x.resize((1,50))
x=np.append(np.ones((1,50)),x,0)
y.resize((1,50))
row,col=x.shape
w = np.mat(np.zeros((row,1)))
x_mat=np.mat(x)#矩阵化后可以直接使用矩阵乘法
y_mat=np.mat(y)
gradient_descent(x_mat,y_mat,w,col)
print(w)
w0=(x_mat*x_mat.T).I*x_mat*y_mat.T#I表示矩阵的逆
print('直接求解的结果')
print(w0)
输出:
第2917次迭代后的结果
[[0.75016253]
[0.06388117]]
直接求解的结果
[[0.75016254]
[0.06388117]]
感觉还行吧,就是调参有点恶心。
多变量就不写了,一样的。