《机器学习算法与实现 —— Python编程与应用实例》-- 第七贴 线性回归模型实战
<p> </p><p><b>引言:</b></p>
<p >书中提到了很多机器学习算法与实现,从中选取了几个典型的算法,在PC上进行了实际体验。本书附带了丰富的电子资料,具体可参考:<a href="https://gitee.com/pi-lab/machinelearning_notebook" style="color:blue; text-decoration:underline">https://gitee.com/pi-lab/machinelearning_notebook</a>;</p>
<p >本贴介绍的典型算法是线性模型,首先介绍书中介绍的一元一次线性拟合算法在pytorch的实现,然后介绍一元三次线性拟合算法的实现;本贴尝试了一元一次方程来拟合的实验过程,并在此基础上,尝试用一元二次方程来做拟合。通过使用一元二次线性函数来拟合,更进一步加深了机器学习的相关概念,目标函数,梯度,训练,极值。</p>
<p > </p>
<p ><b>环境说明:</b></p>
<p > </p>
<p ><b>硬件:</b></p>
<p >我做实验的环境是基于PC,内存</p>
<p >处理器 13th Gen Intel(R) Core(TM) i7-13700KF 3.40 GHz</p>
<p >机带 RAM 16.0 GB (15.7 GB 可用)</p>
<p >系统类型 64 位操作系统, 基于 x64 的处理器</p>
<p >GPU: RXT4060TI-8GB</p>
<p ><b>软件:</b></p>
<p >Windows 11;</p>
<p >Pycharm社区版</p>
<p >Anaconda免费版</p>
<p >Torch</p>
<p >cuda</p>
<p >Git</p>
<p >pip</p>
<p ><b>需要注意的是: </b>建议通过conda来管理python环境,在pycharm中,不同的工程可能需要安装不同的python库,这样管理起来比较方便。</p>
<p >另外,可以将conda/pip等资源的源更换为国内,清华源或者中科大源,否则由于墙的限制,下载一些python包的资源会非常慢。</p>
<p ><b>本书也提到了具体的环境搭建方法,可参考:<a href="https://gitee.com/pi-lab/machinelearning_notebook/blob/master/references_tips/InstallPython.md" style="color:blue; text-decoration:underline">https://gitee.com/pi-lab/machinelearning_notebook/blob/master/references_tips/InstallPython.md</a></b></p>
<p > </p>
<p > </p>
<p ><b>一、一元一次线性算法的pytorch实现</b></p>
<p ><b>1.1模型介绍</b></p>
<p ><b>回顾文章中的一元一次线性模型:</b></p>
<p > </p>
<p >最小二乘法来构造目标函数;</p>
<p >通过梯度下降法可以来找到这个误差函数的最小值,梯度在数学概念中就是导数,对于多元函数,需要求每个变量的偏导数;这个概念需要具备高等数学的知识,将一元一次函数的x和y,看成f(x,y), 分别求x和y的偏导数,则分别是它们的梯度函数;</p>
<p > </p>
<p ><b>梯度有什么意义呢?</b>从几何意义来讲,一个点的梯度值是这个函数变化最快的地方。通俗上理解,我们初中/高中时学到加速度是速度的一阶导数,加速度越快,代表速度变化越快。梯度的含义也差不多。</p>
<p > </p>
<p >针对一元线性回归问题,就是沿着梯度的反方向,不断改变 w 和 b 的值,最终找到一组最好的 w 和 b 使得误差最小<b>。</b></p>
<p ><b>学习率:</b>每次更新w/b的时候,需要决定更新的步长,这个就是学习率,用 η 表示。不同的学习率都会导致不同的结果,学习率太小会导致下降非常缓慢;学习率太大又会导致跳动非常明显。</p>
<p > </p>
<p >按照含义, 每次w、b更新的公式就是:</p>
<p >w=w−η*∂f(w,b)/∂w</p>
<p >b=b−η*∂f(w,b)/∂b</p>
<p > </p>
<p >1.2代码实现</p>
<pre>
<code class="language-python">import torch
import numpy as np
torch.manual_seed(2021)
# 生层测试数据
x_train = np.random.rand(20, 1)
y_train = x_train * 3 + 4 + 3*np.random.rand(20,1)
# 画出图像
import matplotlib.pyplot as plt
plt.plot(x_train, y_train, 'bo')
plt.show()
# 转换成 Tensor
x_train = torch.from_numpy(x_train)
y_train = torch.from_numpy(y_train)
# 定义参数 w 和 b
w = torch.randn(1, requires_grad=True) # 随机初始化
b = torch.zeros(1, requires_grad=True) # 使用 0 进行初始化
# 构建线性回归模型
def linear_model(x):
return x * w + b
#def logistc_regression(x):
# return torch.sigmoid(x*w+b)
y_ = linear_model(x_train)
plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
plt.legend()
plt.show()
# 计算误差
def get_loss(y_, y):
return torch.sum((y_ - y) ** 2)
loss = get_loss(y_, y_train)
print(loss)
# 自动求导
loss.backward()
# 查看 w 和 b 的梯度
print(w.grad)
print(b.grad)
# 更新一次参数
w.data = w.data - 1e-2 * w.grad.data
b.data = b.data - 1e-2 * b.grad.data
y_ = linear_model(x_train)
plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
plt.legend()
plt.show()
for e in range(100):# 进行 100 次更新
y_ = linear_model(x_train)
loss = get_loss(y_, y_train)
w.grad.zero_()# 注意:归零梯度
b.grad.zero_()# 注意:归零梯度
loss.backward()
w.data = w.data - 1e-2 * w.grad.data# 更新 w
b.data = b.data - 1e-2 * b.grad.data# 更新 b
if (e + 1) % 20 == 0:
print('epoch: {}, loss: {}'.format(e, loss.item()))
y_ = linear_model(x_train)
plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
plt.legend()
plt.show()</code></pre>
<p > </p>
<p >1.3结果对比</p>
<p >待拟合数据:</p>
<p > </p>
<p >第一次拟合后的对比:</p>
<p > </p>
<p >中间拟合结果对比:</p>
<p > </p>
<p >最终拟合结果:</p>
<p > </p>
<p > </p>
<p > </p>
<p > </p>
<p ><b>二、一元二次方程拟合尝试</b></p>
<p >用f(x)=(x**2) * w0 + x * w1 + b来拟合,这里要注意,因为本实验中原始数据会变动,因此,每一次执行结果会变动,数据的不同,用一元二次和一元一次模型来拟合的精度可能也会有差别,这并不代表哪一个精度高,对比精度,应该用同样的数据来对比。这里就不做了,有兴趣的坛友,可以稍微改下程序,即可实现。</p>
<p > </p>
<p >原理不多说直接上代码:</p>
<pre>
<code class="language-python">import torch
import numpy as np
torch.manual_seed(2021)
# 生层测试数据
x_train = np.random.rand(20, 1)
y_train = x_train * 3 + 4 + 3*np.random.rand(20,1)
# 画出图像
import matplotlib.pyplot as plt
plt.plot(x_train, y_train, 'bo')
plt.show()
# 转换成 Tensor
x_train = torch.from_numpy(x_train)
y_train = torch.from_numpy(y_train)
# 定义参数 w 和 b
w0 = torch.randn(1, requires_grad=True) # 随机初始化
w1 = torch.randn(1, requires_grad=True) # 随机初始化
b = torch.zeros(1, requires_grad=True) # 使用 0 进行初始化
# 构建线性回归模型
def linear_model(x):
return (x**2) * w0 + x * w1 + b
#def logistc_regression(x):
# return torch.sigmoid(x*w+b)
y_ = linear_model(x_train)
plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
plt.legend()
plt.show()
# 计算误差
def get_loss(y_, y):
return torch.sum((y_ - y) ** 2)
loss = get_loss(y_, y_train)
print(loss)
# 自动求导
loss.backward()
# 查看 w 和 b 的梯度
print(w0.grad)
print(w1.grad)
print(b.grad)
# 更新一次参数
w0.data = w0.data - 1e-2 * w0.grad.data
w1.data = w1.data - 1e-2 * w1.grad.data
b.data = b.data - 1e-2 * b.grad.data
y_ = linear_model(x_train)
plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
plt.legend()
plt.show()
for e in range(100):# 进行 100 次更新
y_ = linear_model(x_train)
loss = get_loss(y_, y_train)
w0.grad.zero_()# 注意:归零梯度
w1.grad.zero_()# 注意:归零梯度
b.grad.zero_()# 注意:归零梯度
loss.backward()
w0.data = w0.data - 1e-2 * w0.grad.data# 更新 w
w1.data = w1.data - 1e-2 * w1.grad.data# 更新 w
b.data = b.data - 1e-2 * b.grad.data# 更新 b
if (e + 1) % 20 == 0:
print('epoch: {}, w0:{}, w1:{}, b:{}, loss: {}'.format(e, w0.data, w1.data, b.data, loss.item()))
y_ = linear_model(x_train)
plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
plt.legend()
plt.show()</code></pre>
<p >原始数据:</p>
<p > </p>
<p >结果:</p>
<p > </p>
<p > </p>
<p > </p>
页:
[1]