# 整体思路: # 1、创建数据 # 2、创建模型(函数),Loss,梯度下降 # 3、随机选取初始参数,根据loss和梯度下降更新参数 # 4、可视化 import torch #深度学习框架 import matplotlib.pyplot as plt #画图的 import random #随机操作 #生成数据函数 def create_data(w, b, data_num): x = torch.normal(0, 1, (data_num, len(w))) #(平均,标准差,规格)data_num为长,w的个数为宽 y = torch.matmul(x, w) + b #matmul矩阵相乘,一定要tensor相乘,即x,w为tensor noise = torch.normal(0, 0.01, y.shape) #添加噪声 y += noise #噪声加到y上 return x, y #假设知道w, b真实值 num = 500 true_w = torch.tensor([8.1, 2, 2, 4]) true_b = torch.tensor(1.1) #X为自变量,Y为标签,生成数据X,Y X, Y = create_data(true_w, true_b, num) #画图 scatter散点图 plt.scatter(X[:, 2], Y, 1) #(自变量,标签,散点的大小);x和y必须相同大小,所以此例要对x进行切片,x为500*4,y为500*1,所以要切一列x出来,该行切的第4列 # 显示绘制的图形 plt.show() # 假设只有数据,要通过X,Y推测w,b # 取数据函数,每次访问这个函数,就能提供一批数据 #一批数据解读:训练模型是先随机w和b,输入x计算出预测值y,然后预测值和真实值通过损失函数算得损失,所有损失相加取平均值,通过梯度下降算法(w1=w0-学习率*损失函数对w0求偏导)训练模型。一批数据指的是不再所有损失相加取平均值算一次loss更新一次模型,而是一批数据算一次loss,然后更新一次模型。 def data_provider(data, label, batchsize): #(数据,标签,每次取多少数据) length = len(label) #label或data都可以 indices = list(range(length)) #把数据的个数(数字)变为一个范围,再转换成列表(即indices是0~length的列表) #如果只有这两行代码,那么是按顺序取数据,一般不能按顺序取数据,做不到普适性随机性,训练要求数据是随机打乱的 random.shuffle(indices) #把数据打乱 for each in range(0, length, batchsize): #(小,大,步长) get_indices = indices[each: each+batchsize] #数据16个一轮取 get_data = data[get_indices] #列表嵌套列表,列表中包含另一个列表作为元素,适合表示多维数据,如矩阵,表格 get_label = label[get_indices] #data,label同时取前batchsize个数据 yield get_data, get_label #有存档点的return。 #试运行 batchsize = 16 # for batch_x, batch_y in data_provider(X, Y, batchsize): #for循环 in后面 的返回值会给到 in 前面的变量 # print(batch_x, batch_y) # break # 定义一个模型(函数) def fun(x, w, b): pred_y = torch.matmul(x, w) + b return pred_y # 定义一个Loss def maeLoss(pred_y, y): #mae平均绝对误差 return torch.sum(abs(pred_y - y)) / len(y) #损失函数Loss,abs取绝对值 #随机梯度下降std,更新参数 def sgd(paras, lr): #(参数,学习率) with torch.no_grad(): #属于这句代码的部分不计算梯度。张量网上所有的计算都会积攒梯度,不是所有梯度我们都需要,我们只要loss对参数的梯度 for para in paras: #从全部参数paras每次取一笔参数para出来 para -= para.grad * lr #梯度下降,不能写成 para = para - para.grad * lr,这句相当于创建了一个新的para,报错 para.grad.zero_() #梯度归零 # 设置学习率,偏置b,随机w0 lr = 0.01 w_0 = torch.normal(0, 0.01, true_w.shape, requires_grad=True) #requires_grad=True这个w需要计算梯度 b_0 = torch.tensor(0.01, requires_grad=True) print(w_0, b_0) # 训练轮数 epochs = 50 for epoch in range(epochs): #对于每一轮来说(一轮是全部数据,该例为500个) data_loss = 0 for batch_x, batch_y in data_provider(X, Y, batchsize): # 从X,Y中取batchsize数量的一批数据 pred_y = fun(batch_x, w_0, b_0) #取完数据后算预测值 loss = maeLoss(pred_y, batch_y) #计算loss loss.backward() #梯度回传,w_0和b_0 sgd([w_0, b_0], lr) #梯度下降更新模型 data_loss += loss #把每一轮(500个)的loss加起来 print("epoch %03d: loss: %.6f"%(epoch, data_loss)) #03d三位整数,.6f六位浮点数。百分号后面替换百分号前面 print("真实值:", true_w, true_b) print("预测值:", w_0, b_0) # 画图, 加.detach().numpy()因为数据在张量网上不能画图,所以要取下来 idx = 3 plt.plot(X[:, idx].detach().numpy(), X[:, idx].detach().numpy()*w_0[idx].detach().numpy()+b_0.detach().numpy()) #画直线,一个x对应一个w,所以要切片 plt.scatter(X[:, idx], Y, 1) #散点图为真实值,上一行直线为预测值 plt.show()
# 张量网