李哥深度学习第二节:linear代码代写
·
代码里写得很详细,嘴对嘴级别的详细
'''
这段代码讲的监督学习里面的线性回归
故事背景
用一个人的身高、颜值、内涵、财富,来推断这个人谈过几次恋爱
'''
# 先导入需要的库
import random
import torch
import matplotlib.pyplot as plt
# 先创建w、b的初始值
#用身高、颜值、内涵、财富,来推断这个人谈过几次恋爱
# 所以数据的维度是4,身高、颜值、内涵、财富各一个维度
# 对应的w也是4个维度
true_w=torch.tensor([8.1,2,2,4])
# b只有一个维度,因为恋爱次数只有一个维度
true_b=torch.tensor(1.1)
# 设置样本数量
num=500
# 创建create_data函数,来生成训练集
def create_data(w,b,data_num):
# x的维度是(data_num,len(w)),比如有500个,就是500个数据
# 每一个数据是一个人,你可以看到这500个人的身高、颜值、内涵、财富各是多少
#这些数据要服从正态分布,均值为0,方差为1
x=torch.normal(mean=0,std=1,size=[data_num,len(w)])
# 使用矩阵相乘matrix multiply,加上b求出y(这里的加上b用了广播的性质,因为维度不同)
# x是(data_num,len(w)),x是500*4,w是[4]
# w按照不同的情况,电脑会将[4]变成4*1或者1*4,所以这里是4*1,要满足矩阵乘法
# y就是500*1,有500个人的数据,y是恋爱次数
y=torch.matmul(x,w)+b
# 要给样本加上噪音
noise=torch.normal(mean=0,std=0.01,size=y.shape)
y+=noise
# x是500 * 4,y是500*1
return x,y
# 调用create_data函数获得数据X,Y
X,Y=create_data(true_w, true_b, num)
'''
matplotlib只能看一个数据和另一个数据之间的关系
这个任务是用身高、颜值、内涵、财富,来推断这个人谈过几次恋爱
自变量是身高、颜值、内涵、财富,因变量是这个人谈过几次恋爱,自变量是4维的
因变量是1维的,我想用matplotlib画图看自变量和因变量之间的关系
但是matplot里面好像画不出来图,只能在4个变量中选一个出来
看这个变量和恋爱次数之间的关系,如果你硬要看,可以画四张图
看四个变量分别和因变量之间的关系,这里图方便,只选了一个自变量和因变量之间的关系
'''
# X是500 * 4,第一个维度是全选,500个人的都要,3是索引,只看第四个维度和因变量之间的关系
# x,y,s分别是自变量、因变量、点的尺寸
# plt.scatter(x=X[:,3],y=Y,s=1)
# plt.show()
batchsize=16
# 定义数据迭代器函数,一次可以获得一个batch的数据
# 就是torch.utils.data.DataLoader
# data是特征数据,就是自变量,label是标签数据,就是因变量,batchsize,批次大小
def data_provider(data,label,batchsize):
# 获取标签的长度
length=len(label)
# 生成样本的索引列表
indices=list(range(length))
# 索引列表的顺序打乱,不能按照顺序取数据
random.shuffle(indices)
# 遍历数据
# each的值是0,16,32,48....
for each in range(0,length,batchsize):
# 利用切片,获得数据
# each=0时,切的是[0,16]
# each = 16时,切的是[16, 32]。。。
get_indices=indices[each:each+batchsize]
# 使用打乱后的索引,只要有索引,就能获得数据
get_data=data[get_indices]
# 获得对应的标签
get_label=label[get_indices]
# 使用yield返回数据,调用一次函数就返回一次数据
# 不用像return那样一次性返回完,占用内存
yield get_data,get_label
# yield的用法自己问gpt,eezz
# 康康数据迭代器能不能用
# for data,label in data_provider(X,Y,batchsize):
# print(data,label)
# # 就看一个batch,就16个
# break
# 设置参数
# 学习率
lr=0.03
# 给定w和b的初始值
w_0=torch.normal(mean=0,std=0.1,size=true_w.shape,requires_grad=True)
b_0=torch.tensor(0.01,requires_grad=True)
# 设置训练轮数
epochs=50
# 定义前向传播函数
def fun(x,w,b):
pred_y=torch.matmul(x,w)+b
return pred_y
# 定义损失函数
def maeLoss(pred_y,y):
# pred_y是预测出来的y是算出来的
# y是真实的y,是标签值,是真实值
# 求的是一个batch的平均损失
return torch.sum(abs(y-pred_y))/len(y)
# 定义优化器,sgd,用的是梯度下降
# params是参数,lr是学习率
def sgd(paras,lr):
with torch.no_grad():
# 奶奶的,这句代码要重要写
# 前面的代码用了require_grad=True
# pytorch会死盯着w和b的每一个操作,记录过程,方便后续计算梯度
# 假设你是小卖部的老板
# w是商品的单价,有no_grad时,你算完当天的利润(loss.backward()算梯度),发现可乐买贵了
# 直接把单价从3变成2.8
# 改账单的时候,不用写账本记录【我为啥改成2.8的种种原因】
# 有no_grad时你要写因为什么原因,在哪里什么时候,把价格改成2.8
# 有no_grad时,你只是直接修改w和b的值,不修改梯度,省内存
# 这句话是禁止在修改参数的过程中,生成无用的新梯度
for para in paras:
# 参数更新公式:参数 = 参数 - 学习率 * 参数梯度(梯度下降核心公式)
# 不能写成 para = para - para.grad*lr(会创建新张量,丢失梯度信息)
para -= para.grad * lr
# 参数梯度清零:避免下一次迭代时梯度累积(每次批次训练都需要重新计算梯度)
# 每次都要梯度清零,因为上一次的梯度会影响下次的梯度
# pytorch中梯度张量活人会进行累加
# 若不手动清零,下一轮新的梯度会和上一轮的旧梯度叠加在一起
# 导致参数更新方向错误,模型无法正常训练
para.grad.zero_()
# 开始训练
for epoch in range(epochs):
data_loss=0
for batch_x,batch_y in data_provider(X,Y,batchsize):
# 前向传播
# 获得预测值
pred_y=fun(batch_x,w_0,b_0)
# 计算损失,预测值和真实值之间的损失
loss=maeLoss(pred_y,batch_y)
# 反向传播,计算梯度
loss.backward()
# 使用优化器,更新参数
sgd([w_0,b_0],lr)
# 计算损失
data_loss+=loss.item()
# 打印当前轮数和对应的总损失:%03d表示3位数字格式化,%.6f表示保留6位小数
print("epoch %03d: loss: %.6f" % (epoch, data_loss))
# 训练结束后,打印真实的权重和偏置
print("真实的函数值是", true_w, true_b)
# 打印训练得到的权重和偏置(已接近真实值)
print("训练得到的参数值是", w_0, b_0)
# 选择第4个特征(索引3)进行可视化对比
idx = 3
# 绘制训练得到的线性模型:需要将张量转换为numpy数组(detach()分离计算图,避免梯度跟踪)
plt.plot(X[:, idx].detach().numpy(), X[:, idx].detach().numpy()*w_0[idx].detach().numpy()+b_0.detach().numpy())
# 绘制原始数据的散点图
plt.scatter(X[:, idx], Y, 1)
# 显示对比图像(散点为原始数据,直线为训练得到的模型)
plt.show()
更多推荐


所有评论(0)