传媒学子 发表于 2024-8-11 10:38

《机器学习算法与实现 —— Python编程与应用实例》-- 第七贴 线性回归模型实战

<p>&nbsp;&nbsp;</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 >&nbsp;</p>

<p ><b>环境说明:</b></p>

<p >&nbsp;</p>

<p ><b>硬件:</b></p>

<p >我做实验的环境是基于PC,内存</p>

<p >处理器&nbsp;&nbsp; 13th Gen Intel(R) Core(TM) i7-13700KF&nbsp;&nbsp; 3.40 GHz</p>

<p >机带 RAM&nbsp;&nbsp;&nbsp;&nbsp; 16.0 GB (15.7 GB 可用)</p>

<p >系统类型&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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 >&nbsp;</p>

<p >&nbsp;</p>

<p ><b>一、一元一次线性算法的pytorch实现</b></p>

<p ><b>1.1模型介绍</b></p>

<p ><b>回顾文章中的一元一次线性模型:</b></p>

<p > &nbsp;</p>

<p >最小二乘法来构造目标函数;</p>

<p >通过梯度下降法可以来找到这个误差函数的最小值,梯度在数学概念中就是导数,对于多元函数,需要求每个变量的偏导数;这个概念需要具备高等数学的知识,将一元一次函数的x和y,看成f(x,y), 分别求x和y的偏导数,则分别是它们的梯度函数;</p>

<p >&nbsp;</p>

<p ><b>梯度有什么意义呢?</b>从几何意义来讲,一个点的梯度值是这个函数变化最快的地方。通俗上理解,我们初中/高中时学到加速度是速度的一阶导数,加速度越快,代表速度变化越快。梯度的含义也差不多。</p>

<p >&nbsp;</p>

<p >针对一元线性回归问题,就是沿着梯度的反方向,不断改变&nbsp;w&nbsp;和&nbsp;b&nbsp;的值,最终找到一组最好的&nbsp;w&nbsp;和&nbsp;b&nbsp;使得误差最小<b>。</b></p>

<p ><b>学习率:</b>每次更新w/b的时候,需要决定更新的步长,这个就是学习率,用&nbsp;&eta;&nbsp;表示。不同的学习率都会导致不同的结果,学习率太小会导致下降非常缓慢;学习率太大又会导致跳动非常明显。</p>

<p >&nbsp;</p>

<p >按照含义, 每次w、b更新的公式就是:</p>

<p >w=w&minus;&eta;*&part;f(w,b)/&part;w</p>

<p >b=b&minus;&eta;*&part;f(w,b)/&part;b</p>

<p >&nbsp;</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 >&nbsp;</p>

<p >1.3结果对比</p>

<p >待拟合数据:</p>

<p > &nbsp;</p>

<p >第一次拟合后的对比:</p>

<p > &nbsp;</p>

<p >中间拟合结果对比:</p>

<p > &nbsp;</p>

<p >最终拟合结果:</p>

<p > &nbsp;</p>

<p > &nbsp;</p>

<p >&nbsp;</p>

<p >&nbsp;</p>

<p ><b>二、一元二次方程拟合尝试</b></p>

<p >用f(x)=(x**2) * w0 + x * w1 + b来拟合,这里要注意,因为本实验中原始数据会变动,因此,每一次执行结果会变动,数据的不同,用一元二次和一元一次模型来拟合的精度可能也会有差别,这并不代表哪一个精度高,对比精度,应该用同样的数据来对比。这里就不做了,有兴趣的坛友,可以稍微改下程序,即可实现。</p>

<p >&nbsp;</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 > &nbsp;</p>

<p >结果:</p>

<p > &nbsp;</p>

<p > &nbsp;</p>

<p >&nbsp;</p>
页: [1]
查看完整版本: 《机器学习算法与实现 —— Python编程与应用实例》-- 第七贴 线性回归模型实战