自学机器学习以来,因为非科班出身,学的很吃力,总觉得自己还差的太多太多,一直希望写下自己的理解,择日不如撞日,今天心血来潮,就这样开始吧,就为写着自己能理解读着舒服,顺带记录自己的成长。打算公开出来,帮助别人不敢说,希望能让人指出自己的错误与不足。
1.理论简述例如我们想要预测一个地方的二手房房价。房价受到房屋使用时间,占地面积,附近是否具备学校医院等因素的影响,我们已经拥有了一些具体数据,但总是有限的,我们希望回归出这样一条线,当我们获得了新的房屋使用时间,平均占地面积......的时候,只需要输入进那条线的公式中,便能预测的与真实价值十分接近的房价。用公式就可以写为:
(此处未引入偏置b,在2.2中有提到)
其中,y表示房价,而不同下标的x便是各种信息,如就是房屋使用的时间,就是它的系数,是决定房屋使用时间会如何影响y房价的,也是我们像要求的。
我们最开始什么也不知道,那就直接来个简单的初始化,比如直接假设所有w都是0,那么无论这些信息是多少,是不是学区房,使用了多少年,算出来房价都是0,这里记作=0,表示预测的值。这显然和真实值差距太大,那么接下来我们需要做的就是去让这些w一点点“找回自己”,让慢慢“找回”本来应该得到的y,我认为这里也贴合了”回归“的主题。
1.1如何找回有了我们最先算的0(当然,绝大多数情况初始化远不会直接用一堆0,为了提高效率,实践中会倾向更合理的初始化,但我才疏学浅,希望将来不久就能学到原理),我们就可以看到它与本来的值的差距,比如该地一套房子价值100万。我们计算的是0万元,那么它们的差就是,我们希望调整w的值来让差变小。可是,我们有时候会遇到初始化不是都是0的情况,如果是其他数字,我们可能得到大于100万的结果,那么差()算下来就变成负数了,但这就没办法继续使用我们希望差变小的思想了。我们并不关心预测值是大于还是小于真实的房价,我们只希望它们之间的差距变小,为了方便计算和理解,我们可以选择用绝对值,或者平方作为目标函数,消除了正负数对我们的影响。
1.1.1如何让目标函数变小,变到最小引入要使用梯度这一概念,梯度实际就是面对高维数据时,函数对各个未知数的偏导组成的向量。如果只有一个未知数,那么梯度就自动变成了导数,两个未知数,梯度就是一个包含两个元素的向量,每个元素代表函数对其中一个未知数的偏导。
那么这也引入一个问题,如果我们目标函数选择的是绝对值,不利于梯度的计算,所以一般使用平方差。(其实从统计学的角度来看,使用平方差即最小二乘法可以利用最大似然估计来解释线性回归)
梯度的每个元素,都代表着函数在这一未知数方向上的变化趋势,若它是正的,表示随着未知数的增大,函数的值会变大,反之亦然。所以梯度代表了目标函数增大的方向(自己用公式写一写就明白了),为了目标函数变小,我们就要让所有未知数减去这个梯度,也就让未知数变得可以使目标函数更小!这也就是梯度下降的原理。
但是还涉及一个叫做学习率的东西,在每次更新w参数的时候,我们为梯度乘上这个学习率来防止我们在寻找最优过程中一步迈太大而一直无法收敛(如更新梯度一下子从最优左侧跳到了右侧,它会反复跳而没办法到达底部)
经过一次次更新,我们的w离本来的w更接近了,算出的也就更贴近真实。
2.实践 2.1numpy实现一元线性回归这里先介绍numpy.random的使用,那几个生成随机数组的函数总是弄混,干脆写一波加深印象
(1)numpy.random.random() 生成一个随机的0 - 1的浮点数,可以通过参数size设置返回数据的size
(2)numpy.random.randint() 生成一个随机整数,参数:low,high指随机数的范围【low,high),若只输入一个数默认为从0到这个数取随机;size同上。
(3)numpy.random.normal() 生成高斯分布随机数,参数:loc均值;scale:标准差;size:同上。
(4)numpy.random.randn() 生成标准正态分布随机数,输入size,如(3,2,4)。
(5)numpy.random.rand() 生成[0, 1)间随机数,接受size。
(6)numpy.random.shuffle() 随机打乱序列
2.1.1代码import numpy as np
# 生成测试数据
x = np.random.random() # 已知的数据
y = 3 * x # 已知的真实值,想要还原的线就是y=3x
w = 0 # 初始化参数,期望最后它变成3
lr = 0.01 # 学习率
# 利用梯度下降训练
i = 0
iterations = 1000
while i < iterations:
y_hat = w * x
loss = (y_hat - y)**2
gradient = 2 * x *(y_hat - y)
w -= lr * gradient
if i % 100 == 0:
print('loss:', loss)
print('new w:', w)
print('**************************')
i += 1
2.1.2结果
我们可以看到w在最后一次迭代中已经离3非常接近了。增加iterations变量数值,可以得到更好的结果。
2.2numpy实现多元线性回归loss: 2.2278246455458866 new w: 0.014852164303639243 ************************** loss: 0.8256554224503053 new w: 1.1827078082661313 ************************** ... loss: 0.0007929095911332126 new w: 2.9436833027245126 ************************** loss: 0.0002938606971338233 new w: 2.96571563625789 **************************
多元的情况则更复杂,数据多以向量/矩阵呈现,最好理解线性代数的知识。在这里,我们也引入一直没提到的偏置b,在真实情况中,我们有理由相信会有偏置存在(例如想要购房需要先提供财产审核花费1000元,无论谁买什么房子都要掏这笔钱),此时原函数变为
其实b作为一个参数,我们完全可以把它并入w,并为x多增加一维,只是这维x一直是1。所以变回来还是wx的形式。
另外,在多元问题中,随着维度的增加,损失函数会越来越大,在实际计算中为了规避这一问题,我们会为目标函数再乘上一个,其中m为x的维度,2是为了消去平方求导后需要乘的2。
2.2.1代码import numpy as np
# 生成测试数据
x = np.random.random(size=(3, 20)) # 20个已知的数据, 3个维度
y = 3 * x[0] - 1.7 * x[1] + 19 * x[2] + 103 # 20个已知的真实值,想要还原的关系是y=3x1 -1.7x2 +19x3 + 103
w = np.array([1., 1., 1., 1.]).reshape(1,-1) # 初始化参数,最后的0是b,前三个是w
x = np.row_stack((x, np.array([1] * 20))) #为x增加一维用于包含b, x.shape=(4,20)
y = y.reshape(1, -1)
lr = 0.01 # 学习率
#梯度下降
i = 0
iterations = 4000
while i < iterations:
y_hat = w.dot(x) * 2 / len(y) # y = 2/m * WX
loss = np.mean((y_hat - y)**2)
gradient = (w.dot(x) - y).dot(x.T) / len(y) # gradient = (WX-y)X.T
w -= lr * gradient
if i % 400 == 0:
print('loss:', loss)
print('gradient:', gradient)
print('new w:', w)
print('**************************')
i += 1
2.2.2结果
可以看到在后面四个参数已经基本都“找回“本来的数值了。
2.3 Pytorch实现多元线性回归loss: 11909.918636979546 gradient: [[-1276.77477851 -1171.65783539 -1264.25078391 -2233.33081243]] new w: [[13.76774779 12.71657835 13.64250784 23.33330812]] ************************** loss: 13053.089999596272 gradient: [[ 2.32618878 1.66949436 0.768882 -2.70471001]] new w: [[ 7.27998388 1.31558645 20.4411289 98.0386967 ]] ************************** ... loss: 13099.121466461558 gradient: [[ 5.66887916e-07 3.93521199e-07 1.94053112e-07 -6.55769995e-07]] new w: [[ 3.00000104 -1.69999928 19.00000036 102.9999988 ]] ************************** loss: 13099.121477600987 gradient: [[ 6.43219484e-08 4.46509049e-08 2.20182870e-08 -7.44066710e-08]] new w: [[ 3.00000012 -1.69999992 19.00000004 102.99999986]] **************************
pytorch使用还不熟练,不对,可以说是刚刚入门,正好练手。
2.3.1代码import numpy as np
import torch
from torch import nn
from torch.autograd import Variable
# 生成测试数据
x = np.random.random((3, 20)) # 20个已知的数据, 3个维度
y = 3 * x[0] - 1.7 * x[1] + 19 * x[2] + 103 # 20个已知的真实值,想要还原的关系是y=3x1 -1.7x2 +19x3 + 103
x = torch.from_numpy(x.T)
y = torch.from_numpy(y.reshape(-1,1))
x = x.type(torch.float32)
y = y.type(torch.float32)
lr = 0.001 # 学习率
# 定义线性回归模型
class linear_regression(nn.Module):
def __init__(self):
super(linear_regression, self).__init__()
self.linear = nn.Linear(3, 1)
def forward(self,x):
return self.linear(x)
if torch.cuda.is_available():
model = linear_regression().cuda()
x = Variable(x).cuda()
y = Variable(y).cuda()
else:
model = linear_regression()
x = Variable(x)
y = Variable(y)
# 定义损失函数
criterion = nn.MSELoss() # Mean square error
# 定义优化器,也就是梯度下降
optimizer = torch.optim.SGD(model.parameters(), lr=lr)
# 训练神经网络
i = 0
iterations = 50000
while i < iterations:
output = model(x)
loss = criterion(y, output)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if i % 5000 == 0:
print('epoch: {0}, loss: {1}'.format(i, loss.data))
print('**************************')
i += 1
# 测试
x_test = np.random.random((3,1))
true_y = 3 * x_test[0] - 1.7 * x_test[1] + 19 * x_test[2] + 103
x_test = torch.from_numpy(x_test.T).cuda()
x_test = x_test.type(torch.float32)
print(true_y)
predict_y = model(x_test)
print(predict_y.data)
2.3.2结果与测试
epoch: 0, loss: 12891.0185546875 ************************** epoch: 5000, loss: 64.59027099609375 ************************** ... epoch: 40000, loss: 0.4187992513179779 ************************** epoch: 45000, loss: 0.2051812708377838 **************************[108.68623593] tensor([[109.1401]], device='cuda:0')
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)