有关张量(Tensor)的创建、常见属性及数据转换 见此链接https://blog.csdn.net/m0_68153457/article/details/141686846?spm=1001.2014.3001.5501
一、Tensor常见操作
1.tensor相关运算
在进行相关运算前,我们要学会获取元素的值,即把单个元素tensor转换为python数值
import torch
def test():
    data1=torch.tensor([10])
    print(data1.item())
    #10
    data2=torch.tensor(5)
    print(data2.item())
    #5
    #以上可看出与tensor的维度无关
    data3=torch.tensor([1,2,3])
    print(data3.item())
    #报错
    #说明若有多个元素则报错
    pass 
if __name__=="__main__":
    test()
 
元素值运算
import torch
def test():
    #注意"-"的区别
    a=torch.randint(1,9,(3,3))
    print(a)
    print("---------")
    #没有"-"时不会替换原始值
    print(a.add(1))
    print(a.sub(1))
    print(a.mul(1))
    print(a.div(1))
    print(a.pow(2))
    print("---------")
    b=a.float()#此处不改为浮点数会报错
    #有"-"时会替换原始值
    b.add_(1)
    b.sub_(1)
    b.mul_(1)
    b.div_(1)
    b.pow_(2)
    print(b)
    pass 
if __name__=="__main__":
    test()
 

阿达玛积
阿达玛积指的是矩阵对应位置的元素相乘,可以使用mul函数或者*来实现
ps:torch.mul(a, b) 是矩阵a和b对应位相乘,a和b的维度必须相等
import torch
def test():
    a=torch.tensor([[1,2],[3,4]])
    b=torch.tensor([[1,1],[2,2]])
    print(a*b)
    print("----------")
    print(a.mul(b))
    
    pass 
if __name__=="__main__":
    test()
 

Tensor相乘
与高等数学中矩阵的乘法类似,n行m列矩阵乘m行p列矩阵得n行p列矩阵
使用@或者matmul完成Tensor的乘法
import torch
def test():
    a=torch.tensor([[1,2],[3,4]])
    b=torch.tensor([[1,1],[2,2]])
    print(a@b)
    print("----------")
    print(a.matmul(b))
    pass 
if __name__=="__main__":
    test()
 

2.索引操作
张量也是有序序列,我们可以根据每个元素在系统内的顺序位置,来找出特定的元素,也就是索引。
n维张量的简单索引
与python索引相似,格式为[start:end:step],在python中step可以去负数,但在tensor中step必须>0,默认值为1
一维张量的索引
import torch
def test():
    a=torch.arange(0,10)
    print(a)
    #tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    print(a[0:5])
    # tensor([0, 1, 2, 3, 4])
    print(a[0:4:2])
    # tensor([0, 2])
#要转换为单独的数需要用上面提到的item()
    pass 
if __name__=="__main__":
    test()
 
二维张量的索引
import torch
def test():
    a=torch.arange(1,17).reshape(4,4)
    print(a)
    print(a[0,1])
    print(a[0][1])
    print(a[::2,::2])
    # 用逗号隔开时,取第一行和第三行的第一列和第三列的元素。
    print(a[::2][::2])
    # 在两个中括号中时,先取了第一行和第三行,构成一个新的二维张量,然后又间隔2并对所有张量进行索引。
    pass 
if __name__=="__main__":
    test()
 

三维张量的索引
与二维张量的索引类似,都是围绕"形状"进行索引
import torch
def test():
    a=torch.arange(1,9).reshape(2,2,2)
    print(a)
    print(a[0,1,1])
    print(a[0][1][1])
    print(a[1,::2,::2])
    print(a[1][::1][::1])
    pass 
if __name__=="__main__":
    test()
 

布尔索引
import torch
def test():
    a=torch.arange(1,9).reshape(2,2,2)
    print(a)
    print("-------")
    # 1. 索引第2个元素大于5的所有行
    print(a[a[:,1]>5])
    print("-------")
    # 索引第1行值大于3的所有的元素所在的列
    print(a[:,a[0]>3])
    print("-------")
    # 3. 第二列是偶数, 且第一列大于2的行
    print(a[(a[:, 1] % 2 == 0) & (a[:, 0] > 2)])
    print("-------")
    pass 
if __name__=="__main__":
    test()
 

索引赋值
import torch
def test():
    a=torch.arange(0,10)
    print(a)
    #tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    a[5]=100
    print(a)
    # tensor([  0,   1,   2,   3,   4, 100,   6,   7,   8,   9])
    pass 
if __name__=="__main__":
    test() 
3.张量拼接
要堆叠的张量必须具有相同的形状。
cat:在现有维度上拼接,不会增加新维度。
stack:在新维度上堆叠,会增加一个维度。
torch.cat
concatenate 的缩写
import torch
def test():
    a=torch.tensor([[1,1],[2,2]])    
    b=torch.tensor([[3,3],[4,4]]) 
    print(torch.cat([a,b],dim=0))
    print(torch.cat([a,b],dim=1))
    pass 
if __name__=="__main__":
    test()
 

torch.stack
import torch
def test():
    a=torch.tensor([[1,1],[2,2]])    
    b=torch.tensor([[3,3],[4,4]]) 
    print(torch.stack([a,b],dim=0))
    print(torch.stack([a,b],dim=1))
    pass 
if __name__=="__main__":
    test()
 

4.形状操作
reshape:更灵活,但涉及内存复制
view:高效,但需要张量在内存中是连续的
①reshape
改变张量的形状,但元素个数不能改变
代码示例:布尔索引示例代码中已有示例
②view
view进行形状变换的特征:
-  
张量在内存中是连续的;
 -  
返回的是原始张量视图,不重新分配内存,效率更高;
 -  
如果张量在内存中不连续,view 将无法执行,并抛出错误。
 
import torch
def task():
    t1=torch.tensor([[1,1],[2,2]])
    #contiguous连续的
    print("正常的张量:",t1.is_contiguous())
    t2=t1.t()
    print("转置后的张量:",t2.is_contiguous())
    print(t2)
    print("----------")
    # -1表示自动计算
    t3=t2.view(2,-1)
    print(t3)
    print("----------")
    tensor=torch.tensor([[1,2,3],[4,5,6]])
    print(tensor)
    tensor_new=tensor.view(3,2)
    print(tensor_new)
    pass
if __name__ == "__main__":
    task() 

③transpose
交换张量的两个维度
import torch
def task():
    data=torch.randint(0,10,(3,3))
    print(data,data.shape)
    data_new=data.transpose(-2,1)
    print(data_new,data_new.shape)
    pass
if __name__ == "__main__":
    task() 

④permute
改变张量的所有维度顺序。与 transpose 类似,但它可以交换多个维度。
import torch
def task():
    data=torch.randint(0,10,(3,3,3))
    print(data,data.shape)
    data_new=data.permute(1,0,2)
    print(data_new,data_new.shape)
    pass
if __name__ == "__main__":
    task() 

⑤flatten
将张量展平为一维向量,
tensor.flatten(start_dim=0, end_dim=-1)
start_dim从哪个维度开始展,end_dim在哪个维度结束展平,默认值为 -1,表示展平到最后一个维度
import torch
def task():
    data=torch.randint(0,10,(3,3))
    print(data,data.shape)
    data_new1=data.flatten(0,-1)
    print(data_new1,data_new1.shape)
    data_new2=data.flatten(0,1)
    print(data_new2,data_new2.shape)
    data_new3=data.flatten(1,-1)
    print(data_new3,data_new3.shape)
    pass
if __name__ == "__main__":
    task() 

⑥升维和降维
-  
unsqueeze:用于在指定位置插入一个大小为 1 的新维度。——升维
 -  
squeeze:用于移除所有大小为 1 的维度,或者移除指定维度的大小为 1 的维度。——降维
 
import torch
def task():
    data=torch.randint(0,10,(3,3))
    print(data,data.shape)
    data_new1=data.squeeze(0)
    print(data_new1,data_new1.shape)
    data_new2=data.unsqueeze(0)
    print(data_new2,data_new2.shape)
    pass
if __name__ == "__main__":
    task()
# tensor([[5, 9, 0],
#         [2, 5, 1],
#         [3, 7, 9]]) torch.Size([3, 3])
# tensor([[5, 9, 0],
#         [2, 5, 1],
#         [3, 7, 9]]) torch.Size([3, 3])
# tensor([[[5, 9, 0],
#          [2, 5, 1],
#          [3, 7, 9]]]) torch.Size([1, 3, 3]) 
5.张量分割
chunk函数能够按照某个维度(dim)对张量进行均匀切分(chunks),并且返回结果是原张量的视图。
chunk(tensor, chunks, dim)
import torch
def task():
    data=torch.randint(0,10,(3,3,4))
    print(data)
    print("--------------")
    data_new=data.chunk(2,2)
    print(data_new)
    pass
if __name__ == "__main__":
    task()
 

6.广播机制
原理类似Numpy广播机制:https://blog.csdn.net/m0_68153457/article/details/140977333?spm=1001.2014.3001.5501
张量广播机制需遵循以下原则
-  
每个张量的维度至少为1
 -  
满足右对齐
 
import torch
def task():
    data1=torch.tensor([[1,1],[2,2]])
    data2=torch.tensor([3,3])
    print(data1.shape,data2.shape)
    print(data1+data2)
    print("--------------")
    data3=torch.tensor([[[1,1],[2,2],[3,3]]])
    data4=torch.tensor([[4,4],[5,5],[6,6]])
    print(data3.shape,data4.shape)
    print(data3+data4)
    pass
if __name__ == "__main__":
    task()
 

7.数学运算
#此节需要记住api的特点特征,代码比较简单,不过多编写。
基本运算
floor: 向下取整;
ceil:向上取整;
round:四舍五入;
trunc:裁剪,只保留整数部分;
frac:只保留小数部分;
fix:向零方向舍入;
%:取余。
三角函数
torch.cos(input,out=None)
torch.cosh(input,out=None) # 双曲余弦函数
torch.sin(input,out=None)
torch.sinh(input,out=None) # 双曲正弦函数
torch.tan(input,out=None)
torch.tanh(input,out=None) # 双曲正切函数
统计学函数
torch.mean(): 计算张量的平均值。
torch.sum(): 计算张量的元素之和。
torch.std(): 计算张量的标准差。
torch.var(): 计算张量的方差。
torch.median(): 计算张量的中位数。
torch.max(): 计算张量的最大值。
torch.min(): 计算张量的最小值。
torch.sort(): 对张量进行排序。
torch.topk(): 返回张量中的前 k 个最大值或最小值。
torch.histc(): 计算张量的直方图。
torch.unique(): 返回张量中的唯一值。
torch.bincount(): 计算张量中每个元素的出现次数。
8.保存和加载
保存:
torch.save(x,position)
加载:
torch.load(position)
9.并行化
在 PyTorch 中,你可以查看和设置用于 CPU 运算的线程数。PyTorch 使用多线程来加速 CPU 运算,但有时你可能需要调整线程数来优化性能
注意:
-  
线程数设置过高可能会导致线程竞争,反而降低性能;
 -  
线程数设置过低可能会导致计算资源未得到充分利用;
 -  
当使用 GPU 进行计算时,线程数设置对性能影响较小,因为 GPU 计算并不依赖于 CPU 线程数。
 
查看线程数
使用 torch.get_num_threads() 来查看当前 PyTorch 使用的线程数
import torch
def get_threads():
    # 获取pytorch运行的线程数
    print(torch.get_num_threads())#4
    pass
if __name__ == "__main__":
    get_threads()
 
 
设置线程数
使用 torch.set_num_threads() 设置 PyTorch 使用的线程数
import torch
def set_get_threads():
    # 设置pytorch运行的线程数
    torch.set_num_threads(4)
    print(torch.get_num_threads())#4
if __name__ == "__main__":
    set_get_threads()
 
 
二、自动微分
自动求导是深度学习框架的重要组成部分。自动微分为深度网络学习中的梯度的计算提供了便捷
1.基础概念
-  
张量
Torch中一切皆为张量,属性requires_grad决定是否对其进行梯度计算。默认是 False,如需计算梯度则设置为True。
 -  
计算图:
torch.autograd通过创建一个动态计算图来跟踪张量的操作,每个张量是计算图中的一个节点,节点之间的操作构成图的边。
 -  
反向传播
使用tensor.backward()方法执行反向传播,从而计算张量的梯度。这个过程会自动计算每个张量对损失函数的梯度。
 -  
梯度
计算得到的梯度通过tensor.grad访问,这些梯度用于优化模型参数,以最小化损失函数。
 
2.计算梯度
使用tensor.backward()方法执行反向传播,从而计算张量的梯度
标量梯度计算
import torch
def task():
    #单标量
    #创建一个张量,只有浮点型和复数型的张量才需要梯度,此处该张量必须为浮点类型
    x=torch.tensor(7, requires_grad=True, dtype=torch.float32)
    y=x**3+x*2+1
    #计算梯度————反向传播
    y.backward()
    #读取梯度值
    print(x.grad)#tensor(149.)
    #多标量
    x1=torch.tensor(8, requires_grad=True, dtype=torch.float32)
    x2=torch.tensor(1, requires_grad=True, dtype=torch.float32)
    y=x1**2+x2*2+2
    y.backward()
    print(x1.grad,x2.grad)#tensor(16.) tensor(2.)
    pass
if __name__ == "__main__":
    task()
 
向量梯度计算
import torch
def task():
    #单向量
    #创建一个张量,只有浮点型和复数型的张量才需要梯度,此处该张量必须为浮点类型
    x=torch.tensor([1,2,3], requires_grad=True, dtype=torch.float32)
    y=x**3+x*2+1
    #需要变为标量:求和或者求均值都可以
    # z=y.sum() #tensor([ 5., 14., 29.])
    z=y.mean()#tensor([1.6667, 4.6667, 9.6667])
    #计算梯度————反向传播
    z.backward()
    #读取梯度值
    print(x.grad)
    #多向量
    x1=torch.tensor([1,2,3], requires_grad=True, dtype=torch.float32)
    x2=torch.tensor([4,5,6], requires_grad=True, dtype=torch.float32)
    y=x1**2+x2*2+2
    #z=y.sum()#tensor([2., 4., 6.]) tensor([2., 2., 2.])
    z=y.mean()#tensor([0.6667, 1.3333, 2.0000]) tensor([0.6667, 0.6667, 0.6667])
    z.backward()
    print(x1.grad,x2.grad)
    pass
if __name__ == "__main__":
    task()
 
 
3.梯度上下文控制
梯度计算的上下文控制和设置对于管理计算图、内存消耗、以及计算效率至关重要
控制梯度计算
import torch
def task():
    x = torch.tensor(11.9, requires_grad=True)
    print(x.requires_grad)#True
    # 1. 默认y的requires_grad=True
    y = x**2 + 2 * x + 3
    print(y.requires_grad)#True
    # 2. 如果不需要y计算梯度 -with进行上下文管理
    with torch.no_grad():
        y = x**2 + 2 * x + 3
    print(y.requires_grad)#False
    # 3. 如果不需要y计算梯度-使用装饰器
    @torch.no_grad()
    def y_fn(x):
        return x**2 + 2 * x + 3
    y = y_fn(x)
    print(y.requires_grad)#False
    pass
if __name__ == "__main__":
    task()
 
 
累计梯度
默认情况下,当我们重复对一个自变量进行梯度计算时,梯度是累加的
import torch
def task():
    x = torch.tensor([1,2,3,4], requires_grad=True,dtype=float)
    for i in range(4):
        y=x**2+x*2+1
        z=y.mean()
        z.backward()
        print(x.grad)
    pass
if __name__ == "__main__":
    task()
# tensor([1.0000, 1.5000, 2.0000, 2.5000], dtype=torch.float64)
# tensor([2., 3., 4., 5.], dtype=torch.float64)
# tensor([3.0000, 4.5000, 6.0000, 7.5000], dtype=torch.float64)
# tensor([ 4.,  6.,  8., 10.], dtype=torch.float64)
 
大多数情况下是不需要梯度累加的
梯度清零
import torch
def test():
    x=torch.tensor([1,2,3],requires_grad=True,dtype=float)
    y=x**2+x*2+2
    z=y.mean()
    #反向传播前先对梯度进行清零
    if x.grad is not None:
        x.grad.zero_()
    z.backward()
    print(x.grad)
    pass
if __name__=="__main__":
    test()
#tensor([1.3333, 2.0000, 2.6667], dtype=torch.float64)
 
示例——求函数最优解
def test1():
    x=np.arange(-10,10,0.1)
    y=x**2+x*3+4
    #绘制表格
    plt.grid()
    plt.plot(x,y)
    plt.show()
def test2():
    #初始化x值
    x=torch.tensor(5,requires_grad=True,dtype=float)
    # 2. 找到损失函数的最小值:迭代进行梯度下降
    for i in range(1000):
        # 2.1 计算损失函数
        y = x**2+x*3+4
        # 2.2 梯度清零
        if x.grad is not None:
            x.grad.data.zero_()
        y.backward()
        # 梯度下降
        x.data.sub_(0.01 * x.grad.data)
        print(x.item(), y.item())
if __name__ == "__main__":
    test1()
    test2()
 

4.梯度计算小结
转换错误
当requires_grad=True时,在调用numpy转换为ndarray时报错如下:
RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
错误处理
使用detach()方法创建张量的叶子节点即可
import torch
def test():
    x=torch.tensor([1,2,3],requires_grad=True,dtype=float)
    print(x.detach().numpy())#[1. 2. 3.]
    print(x.detach())#tensor([1., 2., 3.], dtype=torch.float64)
    pass
if __name__=="__main__":
    test() 
 
叶子结点
detach()产生的张量是作为叶子结点存在的,并且该张量和原张量共享数据,只是该张量不需要计算梯度。
import torch
def test():
    x=torch.tensor([1,2,3],requires_grad=True,dtype=float)
    x_new=x.detach()
    print("是两个东西:",id(x),id(x_new))
    print("数据是共享的:",id(x.data),id(x_new.data))
    x_new[0]=100
    print(x,x_new)
    
    pass
if __name__=="__main__":
    test() 

三、手动构建模型
| 名词 | 定义 | 
|---|---|
| Epoch(时代) | 使用训练集的全部数据对模型进行一次完整训练,被称为“一代训练” | 
| Batch(批处理) | 使用训练集中的一小部分样本对模型权重进行一次反向传播的参数更新,这一小部分样本被称为“一批数据” | 
| Iteration(迭代) | 使用一个Batch数据对模型进行一次参数更新的过程,被称为“一次训练” | 
1.数据处理
构建数据集
使用 sklearn 的 make_regression 方法来构建一个模拟的回归数据集。
    make_regression 方法的参数解释:
     - n_samples: 生成的样本数量,决定了数据集的规模。
     - n_features: 生成的特征数量,决定了数据维度。
     - noise: 添加到目标变量的噪声标准差,用于模拟真实世界数据的不完美。
     - coef: 如果为 True, 会返回生成数据的真实系数,用于了解特征与目标变量间的真实关系。
     - random_state: 随机数生成的种子,确保在多次运行中能够复现相同的结果。
    返回:
     - X: 生成的特征矩阵。
     - y: 生成的目标变量。
     - coef: 如果在调用时 coef 参数为 True,则还会返回真实系数。
——coef 系数
——bias偏差
import torch
import random
import math
import numpy as np
from sklearn.datasets import make_regression
import pandas as pd
def build_dataset():
    n=random.randint(1,5)
    x,y,coef=make_regression(n_samples=1000,n_features=5,bias=14.5,noise=n,coef=True,random_state=0)
    #将数据转换为张量
    x=torch.tensor(x,dtype=float)
    y=torch.tensor(y,dtype=float)
    coef=torch.tensor(coef,dtype=float)
    return x,y,coef
if __name__=="__main__":
    #构建一批数据集
    x,y,coef=build_dataset()
    #将特征数据x转换为DataFrame,并添加列名
    features_names=[f"feature_{i+1}" for i in range(x.shape[1])]
    x_df=pd.DataFrame(x,columns=features_names)
    #创建包含目标变量y的Series
    y_series=pd.Series(y,name="Target")
    #将特征和目标变量数据合并为一个完整的数据集
    data_df=pd.concat([x_df,y_series],axis=1)
    print(f"前五行的数据:\n{data_df.head()}")
    print(f"生成数据的真实系数:\n{coef}")
 

构建数据加载器
import torch
import random
import math
import numpy as np
from sklearn.datasets import make_regression
import pandas as pd
def build_dataset():
    n=random.randint(1,5)
    x,y,coef=make_regression(n_samples=1000,n_features=5,bias=14.5,noise=n,coef=True,random_state=0)
    #将数据转换为张量
    x=torch.tensor(x,dtype=float)
    y=torch.tensor(y,dtype=float)
    coef=torch.tensor(coef,dtype=float)
    return x,y,coef
def data_loader(x,y,batch_size=16):
    # 将数据集转换为迭代器,以便在训练过程中进行批量处理
    #获取样本数量
    num_samples=x.shape[0]
    #构建数据索引
    index=list(range(num_samples))
    #打乱数据顺序
    random.shuffle(index)
    #计算总的批次数量:向上取整
    num_batches=math.ceil(num_samples / batch_size)
    for i in range(num_batches):
        start=i*batch_size
        end=min((i+1)*batch_size,num_samples)
        #切片数据
        x_train=x[index[start:end]]
        y_train=y[index[start:end]]
        #异步响应数据集
        yield x_train,y_train
 
运行结果就省略了吧。。。。太长了
2.模型函数
def linear_regression(x, w, b):
     return torch.matmul(x, w) + b
-  
点积运算(Matmul):
torch.matmul(x, w)计算输入 xx 与权重 ww 的矩阵乘法。这里假设 xx 是一个一维向量或二维张量,而 ww 是一个权重矩阵,其形状适合进行矩阵乘法。 -  
加偏置:然后将上述结果与偏置项 bb 相加。偏置项 bb 通常是标量或向量,它的尺寸应该与 xwxw 的结果相匹配,使得两者能够进行逐元素加法操作。
 
示例
如果我们有一个二维输入数据集 xx,每个样本有多个特征,ww 就是一个矩阵,其中每一行对应于输入特征的一个权重向量。bb 是一个向量,每个元素对应输出维度上的偏置。
例如,如果 xx 是一个形状为 (N, D) 的张量(N 个样本,每个样本有 D 个特征),ww 是一个形状为 (D, M) 的权重矩阵(从 D 个输入特征映射到 M 个输出),那么 torch.matmul(x, w) 的结果将是 (N, M) 形状的张量。接着加上一个形状为 (M,) 的偏置向量 bb,最终输出也将是 (N, M) 形状的张量。
这就是函数 linear_regression(x, w, b) 的基本作用。
import torch
x=torch.tensor([[1],[2],[3]])
w=torch.tensor([[1,2,3]])
b=torch.tensor([1])
def linear_regression(x,w,b):
    y=torch.matmul(x,w)+b
    return y
y=linear_regression(x,w,b)
print(y)
# tensor([[ 2,  3,  4],
#         [ 3,  5,  7],
#         [ 4,  7, 10]])
 
 
3.损失函数
def mean_squared_error(y_pred, y_true):
     return torch.mean((y_pred - y_true) ** 2)
函数 mean_squared_error 实现了均方误差(Mean Squared Error, MSE)的计算。均方误差是一个常用的损失函数,用于评估预测值 y_{\text{pred}}ypred 与真实值 y_{\text{true}}ytrue 之间的差异。它是通过计算预测值与真实值之间差的平方的平均值来得到的。
参数说明
y_pred: 预测值,通常是一个张量,形状为 (N,) 或 (N, 1),其中 N 是样本数量。y_true: 真实值,也是一个张量,形状与y_pred相同。
返回值
- 返回一个标量张量,表示所有样本的均方误差。
 
详细步骤
- 计算差值:
diff = y_pred - y_true计算每个样本的预测值与真实值之间的差。 - 计算平方:
squared_diff = diff ** 2对每个差值求平方。 - 计算均值:
mse = torch.mean(squared_diff)求所有样本差值平方的平均值。 
示例代码
import torch
y_true = torch.tensor([1.0, 2.0, 3.0])
y_pred = torch.tensor([1.5, 1.8, 2.9])
# 定义均方误差函数
def mean_squared_error(y_pred, y_true):
    diff = y_pred - y_true
    squared_diff = diff ** 2
    mse = torch.mean(squared_diff)
    return mse
# 计算均方误差
mse = mean_squared_error(y_pred, y_true)
print("均方误差:", mse.item())  # 使用 .item() 将张量转换为 Python 数字
# 均方误差: 0.10000000149011612 
输出的结果 均方误差 (MSE)表示预测值与真实值之间的平均误差大小。越接近于 0,表示预测越准确。
4.优化器
def sgd(w, b, dw, db, learning_rate, batch_size):
     w.data -= learning_rate * dw.data / batch_size
     b.data -= learning_rate * db.data / batch_size
     return w, b
参数说明
w:权重张量,通常是模型中的权重参数。b:偏置张量,通常是模型中的偏置参数。dw:权重的梯度,即损失函数相对于权重的导数。db:偏置的梯度,即损失函数相对于偏置的导数。learning_rate:学习率,控制每次更新步长的大小。batch_size:批量大小,表示当前使用的样本数量。
返回值
- 返回更新后的权重 
w和偏置b。 
详细步骤
- 计算权重更新:
w.data -= learning_rate * dw.data / batch_size,这里的dw.data是权重的梯度,通过除以batch_size并乘以learning_rate来确定更新步长。 - 计算偏置更新:
b.data -= learning_rate * db.data / batch_size,这里的db.data是偏置的梯度,同样通过除以batch_size并乘以learning_rate来确定更新步长。 
示例代码
import torch
def sgd(w, b, dw, db, learning_rate, batch_size):
    w.data -= learning_rate * dw.data / batch_size
    b.data -= learning_rate * db.data / batch_size
    return w, b
# 初始化权重和偏置
w = torch.tensor([0.5], requires_grad=True)
b = torch.tensor([0.0], requires_grad=True)
# 假设我们已经计算出了梯度
dw = torch.tensor([0.2])
db = torch.tensor([0.1])
# 设置学习率和批量大小
learning_rate = 0.01
batch_size = 10
# 更新权重和偏置
w, b = sgd(w, b, dw, db, learning_rate, batch_size)
print("更新后的权重:", w.item())
print("更新后的偏置:", b.item())
# 更新后的权重: 0.4997999966144562
# 更新后的偏置: -0.00010000000474974513 
5.参数初始化
# 初始化参数
 def initialize_params(n_features):
     # 随机初始化权重w,并将偏置b初始化为0
     w = torch.randn(n_features, requires_grad=True, dtype=torch.float32)
     b = torch.tensor(0.0, requires_grad=True, dtype=torch.float32)
     return w, b
函数 initialize_params 用于初始化线性回归模型中的参数,包括权重 w 和偏置 b
参数说明
n_features:输入特征的数量,即每个样本的维度。
返回值
- 返回初始化后的权重 
w和偏置b。 
详细步骤
-  
初始化权重
w:- 使用 
torch.randn函数生成一个形状为(n_features,)的一维张量。 requires_grad=True表示这个张量需要计算梯度,以便在反向传播过程中更新。dtype=torch.float32指定张量的数据类型为 32 位浮点数。
 - 使用 
 -  
初始化偏置
b:- 使用 
torch.tensor创建一个标量张量,值为 0.0。 - 同样设置 
requires_grad=True以便计算梯度。 - 数据类型同样为 
torch.float32。 
 - 使用 
 
示例代码
import torch
# 初始化参数
def initialize_params(n_features):
    # 随机初始化权重 w,并将偏置 b 初始化为 0
    w = torch.randn(n_features, requires_grad=True, dtype=torch.float32)
    b = torch.tensor(0.0, requires_grad=True, dtype=torch.float32)
    return w, b
# 假设我们有 3 个特征
n_features = 3
# 初始化参数
w, b = initialize_params(n_features)
# 假设我们有一些输入数据 x
x = torch.tensor([[1.0, 2.0, 3.0],[4.0, 5.0, 6.0]], dtype=torch.float32)
# 定义线性回归函数
def linear_regression(x, w, b):
    return torch.matmul(x, w) + b
# 计算预测值
y_pred = linear_regression(x, w, b)
print("预测值:", y_pred)
# 预测值: tensor([ 6.3001, 11.5534], grad_fn=<AddBackward0>) 
 
6.训练函数和项目整合
import torch
from sklearn.datasets import make_regression
import random
import math
# 数据集创建
def build_datasets(n_features=1):
    noise = 3
    bias = 0.0
    x, y, ceof = make_regression(
        n_samples=1000,  # 样本数量
        n_features=n_features,  # 特征数量
        noise=noise,
        coef=True,  # 是否返回系数
        bias=bias,  # 偏置
        random_state=42,  # 随机数种子
    )
    # 转换为张量
    x = torch.tensor(x, dtype=torch.float32)
    y = torch.tensor(y, dtype=torch.float32)
    ceof = torch.tensor(ceof, dtype=torch.float32)
    bias = torch.tensor(bias, dtype=torch.float32)
    return x, y, ceof, bias
# 数据加载器
def data_loader(x, y, batch_size):
    # 样本数量
    length = x.shape[0]
    # 组装索引列表
    idx = [i for i in range(length)]
    # 打乱
    random.shuffle(idx)
    # 总共需要取多少次:32
    batch_num = math.ceil(length / batch_size)
    for i in range(batch_num):
        index = idx[i * batch_size : (i + 1) * batch_size]
        yield x[index], y[index]
# 线性模型
def model(x, w, b):
    return torch.matmul(x, w) + b
# 参数初始化小函数
# def init_wandb(feature):
#     w = torch.randn(feature, 1, requires_grad=True)
#     b = torch.zeros(1, requires_grad=True)
#     return w, b
def init_wandb(feature):
    w = torch.randn(feature, requires_grad=True)
    b = torch.tensor(0.0, requires_grad=True, dtype=torch.float32)
    return w, b
# 损失函数
def mseLoss(ypred, label):
    return torch.mean((ypred - label) ** 2)
# 优化器
def sgd(w, b, lr=1e-2, batch_size=1):
    w.data = w.data - lr * (w.grad.data)
    b.data = b.data - lr * (b.grad.data)
    return w, b
def train():
    # 数据集
    feature = 5
    x, y, ceof, bias = build_datasets(feature)
    # 定义一些基本的训练参数
    epochs = 100  # 训练代次
    lr = 1e-2  # 学习率
    batch_size = 32  # 批处理大小
    # 模型参数初始化
    w, b = init_wandb(feature)
    for i in range(epochs):
        # 构建数据加载器
        for x_batch, target in data_loader(x, y, batch_size):
            # 使用模型预测值
            ypred = model(x_batch, w, b)
            # 计算损失
            loss = mseLoss(ypred, target)
            # 梯度清零
            if w.grad is not None:
                w.grad.zero_()
            if b.grad is not None:
                b.grad.zero_()
            # 反向传播
            loss.backward()
            # 梯度更新
            w, b = sgd(w, b, lr, batch_size)
        print(f"Epoch {i}:", w, b)#略
    print("训练的权重参数:", w)
    print("真实的权重参数:", ceof)
#训练的权重参数: tensor([28.4494, 46.0176, 16.7525, 24.6441, 19.0856], requires_grad=True)
#真实的权重参数: tensor([28.6280, 46.0712, 16.8237, 24.7463, 18.9935])
if __name__ == "__main__":
    train()
 
                















![[Python]之深拷贝与浅拷贝](https://i-blog.csdnimg.cn/direct/394560e4686c473daf08c63cfff98054.png#pic_center)

