pytorch基础操作
前言:#
此篇主要涉及pytorch的入门内容,主要是张量的基础操作以及使用pytorch进行反向传播,最后还简要介绍了pytorch的一系列常用的基础组件,以便对后续更深层次的学习任务奠定基础。
一、创建张量#
1.1 简单张量#
根据已有数据创建张量,torch.tensor默认数字类型是float32
def test0():
# 创建标量
data = torch.tensor(10)
print(data)
# 使用numpy数组创建张量
data = np.random.randn(2,3)
data = torch.tensor(data)
print(data)
# 使用列表创建多维张量
data = [[[i for i in range(10)],[j for j in range(10,20)]]]
data = torch.tensor(data)
print(data)
test0()tensor(10) tensor([[ 0.5558, 0.0213, 0.4975], [ 0.1977, 2.0550, -0.3598]], dtype=torch.float64) tensor([[[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]]])
创建指定形状张量
def test1():
# 创建两行三列张量
data = torch.Tensor(2,3)
print(data)
# 创建指定值张量
data = torch.Tensor([2,3])
print(data)
test1()tensor([[1., 1., 1.], [1., 1., 1.]]) tensor([2., 3.])
创建指定类型张量,传递数据如果不匹配会发生类型转换
def test2():
# 创建int类型张量
data = torch.IntTensor(2,3)
print(data)
# 创建float类型张量
data = torch.FloatTensor(2,3)
print(data)
test2()tensor([[772014944, 1301, 0], [ 0, 0, 0]], dtype=torch.int32) tensor([[0., 0., 0.], [0., 0., 0.]])
1.2 线性张量和随机张量#
创建线性张量
def test3():
# 创建指定步长张量
# arange(start,end,步长)
data = torch.arange(0,10,2)
data = torch.Tensor(data)
print(data)
# 指定区间指定元素个数
# linspace(start,end,元素个数)
data = torch.linspace(0,10,100)
data = torch.Tensor(data)
print(data)
test3()tensor([0, 2, 4, 6, 8]) tensor([ 0.0000, 0.1010, 0.2020, 0.3030, 0.4040, 0.5051, 0.6061, 0.7071, 0.8081, 0.9091, 1.0101, 1.1111, 1.2121, 1.3131, 1.4141, 1.5152, 1.6162, 1.7172, 1.8182, 1.9192, 2.0202, 2.1212, 2.2222, 2.3232, 2.4242, 2.5253, 2.6263, 2.7273, 2.8283, 2.9293, 3.0303, 3.1313, 3.2323, 3.3333, 3.4343, 3.5354, 3.6364, 3.7374, 3.8384, 3.9394, 4.0404, 4.1414, 4.2424, 4.3434, 4.4444, 4.5455, 4.6465, 4.7475, 4.8485, 4.9495, 5.0505, 5.1515, 5.2525, 5.3535, 5.4545, 5.5556, 5.6566, 5.7576, 5.8586, 5.9596, 6.0606, 6.1616, 6.2626, 6.3636, 6.4646, 6.5657, 6.6667, 6.7677, 6.8687, 6.9697, 7.0707, 7.1717, 7.2727, 7.3737, 7.4747, 7.5758, 7.6768, 7.7778, 7.8788, 7.9798, 8.0808, 8.1818, 8.2828, 8.3838, 8.4848, 8.5859, 8.6869, 8.7879, 8.8889, 8.9899, 9.0909, 9.1919, 9.2929, 9.3939, 9.4949, 9.5960, 9.6970, 9.7980, 9.8990, 10.0000])
创建随机张量
def test4():
# 随机种子
torch.random.manual_seed(42)
data = torch.randn(2,3)
data = torch.Tensor(data)
print(data)
test4()tensor([[ 0.3367, 0.1288, 0.2345], [ 0.2303, -1.1229, -0.1863]])
1.3 指定值张量#
def test5():
# 创建全为零的张量
data = torch.zeros(2,3)
print(data)
# 根据其他张量的形状创建全0张量
data1 = torch.zeros_like(data)
print(data1)
# 创建全为一的张量,ones_like同理
data = torch.ones(2,3)
print(data)
# 创建指定值张量,torch.fill(形状,填充值),full_like同理
data = torch.full([2,3],6)
print(data)
test5()tensor([[0., 0., 0.], [0., 0., 0.]]) tensor([[0., 0., 0.], [0., 0., 0.]]) tensor([[1., 1., 1.], [1., 1., 1.]]) tensor([[6, 6, 6], [6, 6, 6]])
1.4 张量元素类型转换#
def test6():
# type函数转换
data = torch.full([2,3],6)
print("转换前",data.dtype)
# 会返回一个新类型的变量而不是直接修改原张量
data1 = data.type(torch.FloatTensor)
print("转换后",data1.dtype)
# 使用具体类型函数转换
data2 = data.float()
print("转换后",data2.dtype)
test6()转换前 torch.int64 转换后 torch.float32 转换后 torch.float32
二、数值计算#
2.1 张量的基本运算#
加减乘除相反数
def test0():
torch.random.manual_seed(40)
# torch.randint(start,end,形状)
data = torch.randint(0,10,[2,3])
print(data)
# 计算完成后会产生一个新的张量,满足计算广播机制
# 加法
tensor = torch.randint(0,10,[2,1])
print(tensor)
data_add = data.add(tensor)
print("data_add",data_add)
# 减法
data_sub = data.sub(tensor)
print("data_sub",data_sub)
# 乘法
data_mul = data.mul(data_sub)
print("data_mul",data_mul)
# 除法
data_div = data.div(tensor)
print("data_div",data_div)
# 取相反数
data_neg = data.neg()
print("data_neg",data_neg)
test0()tensor([[8, 3, 5], [7, 2, 4]]) tensor([[6], [3]]) data_add tensor([[14, 9, 11], [10, 5, 7]]) data_sub tensor([[ 2, -3, -1], [ 4, -1, 1]]) data_mul tensor([[16, -9, -5], [28, -2, 4]]) data_div tensor([[1.3333, 0.5000, 0.8333], [2.3333, 0.6667, 1.3333]]) data_neg tensor([[-8, -3, -5],
def test2():
# 使用mul函数
data1 = torch.tensor([[1,2],[3,4]])
data2 = torch.tensor([[5,6],[7,8]])
data = data1.mul(data2)
print(data)
# 使用*
data = data1*data2
print(data)
test2() [-7, -2, -4]])修改原数据的运算,不需要额外变量来接收运算结果
直接在原有运算方法名后面加下划线
def test1():
torch.random.manual_seed(40)
data = torch.randint(0,10,[2,3])
print(data)
tensor = torch.randint(0,10,[2,1])
print(tensor)
data.add_(tensor)
print("result",data)
test1()tensor([[8, 3, 5], [7, 2, 4]]) tensor([[6], [3]]) result tensor([[14, 9, 11], [10, 5, 7]])
2.2 阿达玛积#
阿达玛积:张量对应位置元素相乘
def test2():
# 使用mul函数
data1 = torch.tensor([[1,2],[3,4]])
data2 = torch.tensor([[5,6],[7,8]])
data = data1.mul(data2)
print(data)
# 使用*
data = data1*data2
print(data)
test2()tensor([[ 5, 12], [21, 32]]) tensor([[ 5, 12], [21, 32]])
2.3 点积运算#
def test3():
# 使用@运算符直接对两个二维矩阵进行计算
data1 = torch.tensor([[1,2],[3,4],[5,6]])
data2 = torch.tensor([[5,6],[7,8]])
data = data1@data2
print("@",data)
# 使用mm函数,要求输入张量形状都是二维的
data1 = torch.tensor([[1,2],[3,4],[5,6]])
data2 = torch.tensor([[5,6],[7,8]])
data = torch.mm(data1,data2)
print("mm",data)
# 使用bmm函数运算,要求输入数据必须是三维
torch.random.manual_seed(40)
# torch.randn(批次,行,列)
data1 = torch.randn(3,4,5)
print(data1)
data2 = torch.randn(3,5,8)
data = torch.bmm(data1,data2)
print("bmm",data.shape)
# 使用matmul函数
data1 = torch.randn(4,5)
data2 = torch.randn(5,6)
data = torch.matmul(data1,data2)
print("matmul1",data.shape)
data1 = torch.randn(3,4,5)
data2 = torch.randn(3,5,8)
data = torch.matmul(data1,data2)
print("matmul2",data.shape)
test3()@ tensor([[19, 22], [43, 50], [67, 78]]) mm tensor([[19, 22], [43, 50], [67, 78]]) tensor([[[-0.2367, 1.8109, 0.1966, -0.7150, 0.1041], [ 0.8893, -0.4212, -0.5279, 0.9281, 0.8614], [-0.5856, 1.4157, -1.9983, -0.7397, -1.1917], [ 0.0635, -1.3966, 0.4813, -1.2866, 0.1643]],
[[ 0.4827, 0.4881, -1.8173, 1.0127, 1.3802], [ 0.0903, 0.7811, 0.0891, 0.1531, -0.2344], [ 0.0868, -1.5610, -0.5121, -0.9283, 1.0775], [ 0.1593, -1.8646, 0.5430, 1.4348, 1.1829]],
[[-0.3522, 0.9055, -0.1248, -0.1938, -0.3097], [-1.4255, 1.2274, -1.7690, -1.6122, 0.4332], [ 1.6599, 1.6113, 0.1054, 0.1470, 1.1172], [-0.2970, -0.1661, -0.8077, -0.0500, 0.1276]]]) bmm torch.Size([3, 4, 8]) matmul1 torch.Size([4, 6]) matmul2 torch.Size([3, 4, 8])
三、 数值转换#
3.1 tensor张量转numpy数组#
转换后的两个数据共享内存地址,可以使用copy()来区分开来
def test0():
data_tensor = torch.tensor([1,2,3])
# 将张量转换为numpy数组
data_np = data_tensor.numpy()
print(data_tensor,type(data_tensor))
print(data_np,type(data_np))
# 此时两个数据共享内存
data_tensor[0] = 2
print("修改tensor后")
print(data_tensor)
print(data_np)
data_np[0] = 3
print("修改numpy后")
print(data_tensor)
print(data_np)
# 使用copy函数实现不共享内存
data_tensor = torch.tensor([1,2,3])
data_np = data_tensor.numpy().copy()
data_tensor[0] = 2
print("修改copy的tensor后")
print(data_tensor)
print(data_np)
data_np[0] = 3
print("修改copy的numpy后")
print(data_tensor)
print(data_np)
test0()tensor([1, 2, 3]) <class 'torch.Tensor'> [1 2 3] <class 'numpy.ndarray'> 修改tensor后 tensor([2, 2, 3]) [2 2 3] 修改numpy后 tensor([3, 2, 3]) [3 2 3] 修改copy的tensor后 tensor([2, 2, 3]) [1 2 3] 修改copy的numpy后 tensor([2, 2, 3]) [3 2 3]
3.2 numpy数组转tensor张量#
def test1():
data_numpy = np.array([2,3,4])
# from_numpy函数,默认共享内存
data_tensor1 = torch.from_numpy(data_numpy)
# 设置不共享地址
data_tensor_copy = torch.from_numpy(data_numpy.copy())
print(data_tensor1)
# 使用tensor,默认不共享内存
data_tensor2 = torch.tensor(data_numpy)
print(data_tensor2)
test1()tensor([2, 3, 4]) tensor([2, 3, 4])
3.3 标量张量和数字的转换#
使用item()提取数字,只适用于张量中只有一个元素的情况
def test2():
data1 = torch.tensor(0)
data2 = torch.tensor([10])
data3 = torch.tensor([[20]])
print(data1.shape,data2.shape,data3.shape)
num1 = data1.item()
num2 = data2.item()
num3 = data3.item()
print(num1,num2,num3)
test2()torch.Size([]) torch.Size([1]) torch.Size([1, 1]) 0 10 20
四、拼接操作#
4.1 torch.cat(连接操作)#
- 功能:沿指定维度连接多个张量,输入张量的形状(除拼接维度外)必须完全相同。
- 不新增维度,仅扩展现有维度的大小。
- 适用于需要合并数据但无需新增维度的场景(如拼接多个特征向量)。
import torch
x = torch.randn(2, 3)
y = torch.randn(2, 3)
# 沿维度0拼接(行方向)
z_cat = torch.cat([x, y], dim=0) # 输出形状:(4, 3)4.2 torch.stack(堆叠操作)#
- 功能:在新创建的维度上堆叠多个张量,所有输入张量的形状必须完全一致。
- 新增一个维度,堆叠后的张量比输入张量多一维。
- 适用于需要创建批次维度或组合多个张量的场景(如创建图像批次)。
# 相同输入张量x和y
z_stack = torch.stack([x, y], dim=0) # 输出形状:(2, 2, 3)关键差异总结
- 维度变化:
cat不新增维度,stack会新增一个维度。 - 输入要求:
cat允许拼接维度的大小不同(其他维度相同),stack要求所有维度完全一致。 - 典型用途:
cat用于合并数据序列,stack用于构建批次或组合张量。
五、索引操作#
5.1 简单行列索引#
def test0():
torch.random.manual_seed(42)
data = torch.randint(0,10,(4,5))
print(data)
# 获得某行某列元素
print(data[0])
print(data[0,1])
print(data[0][1])
# 左行右列,左闭右开
print(data[:3,2:3])
print(data[:3][2:3])
test0()tensor([[2, 7, 6, 4, 6], [5, 0, 4, 0, 3], [8, 4, 0, 4, 1], [2, 5, 5, 7, 6]]) tensor([2, 7, 6, 4, 6]) tensor(7) tensor(7) tensor([[6], [4], [0]]) tensor(7)
5.2 布尔索引#
def test1():
torch.random.manual_seed(42)
data = torch.randint(0,100,(4,5))
print(data)
# 获取所有大于30的元素
print(data[data>30])
# 获取第二列有元素大于20的行
print(data[data[:,1]>=20])
test1()tensor([[42, 67, 76, 14, 26], [35, 20, 24, 50, 13], [78, 14, 10, 54, 31], [72, 15, 95, 67, 6]]) tensor([42, 67, 76, 35, 50, 78, 54, 31, 72, 95, 67]) tensor([[42, 67, 76, 14, 26], [35, 20, 24, 50, 13]])
5.3 多维索引#
def test2():
torch.random.manual_seed(42)
data = torch.randint(0,100,(3,4,5))
print(data)
print(data[0,:,:])
print(data[:,0,:])
print(data[:,:,0])
test2()tensor([[[42, 67, 76, 14, 26], [35, 20, 24, 50, 13], [78, 14, 10, 54, 31], [72, 15, 95, 67, 6]],
[[49, 76, 73, 11, 99], [13, 41, 69, 87, 19], [72, 80, 75, 29, 33], [64, 39, 76, 32, 10]],
[[86, 22, 77, 19, 7], [23, 43, 94, 93, 77], [70, 9, 70, 39, 86], [99, 15, 84, 78, 8]]]) tensor([[42, 67, 76, 14, 26], [35, 20, 24, 50, 13], [78, 14, 10, 54, 31], [72, 15, 95, 67, 6]]) tensor([[42, 67, 76, 14, 26], [49, 76, 73, 11, 99], [86, 22, 77, 19, 7]]) tensor([[42, 35, 78, 72], [49, 13, 72, 64], [86, 23, 70, 99]])
六、形状操作#
6.1 reshape()函数#
def test0():
torch.random.manual_seed(42)
data = torch.randint(0,10,[4,5])
print(data.shape)
# 修改张量形状
new_data = torch.reshape(data,[2,10])
print(new_data.shape)
# 使用-1自动匹配形状
new_data = torch.reshape(data,[-1,20])
print(new_data.shape)
test0()torch.Size([4, 5]) torch.Size([2, 10]) torch.Size([1, 20])
6.2 transpose()函数和permute()函数#
transpose只是简单进行维度交换,一次只能交换两个维度,permute函数在transpose基础上可以一次交换多个维度
def test1():
torch.random.manual_seed(42)
data = torch.randint(0,10,[3,4,5])
print(data.shape)
print(data)
print("-"*50)
# reshape重新计算形状
data_reshape = torch.reshape(data,[4,3,5])
print(data_reshape.shape)
print(data_reshape)
print("-"*50)
data_transpose = torch.transpose(data,0,1)
print(data_transpose.shape)
print(data_transpose)
print("-"*50)
data_permute = torch.permute(data,(2,0,1))
print(data_permute.shape)
print(data_permute)
test1()6.3 view()函数和contigous()函数#
def test1():
# view函数的使用
data = torch.tensor([[10,20,30],[40,50,60]])
data_view = data.view(3,2)
print(data_view.shape)
# 使用is_contigous判断张量是否是连续的内存空间
print(data.is_contiguous())
# 如果不是连续的内存空间,不可以使用view来更改维度
data_transpose = torch.transpose(data,0,1)
print(data_transpose.is_contiguous())# 非连续
# print(data_transpose.view(2,3))# 非连续地址,报错
# 使用contiguous()将非连续内存改为连续内存
print(data_transpose.contiguous().view(2,3))
test1()torch.Size([3, 2]) True False tensor([[10, 40, 20], [50, 30, 60]])
6.4 squeeze()函数和unsqueeze()函数#
def test2():
# squeeze函数降维,去除数据中的1维度
data = torch.randint(1,10,[1,3,1,5])
print(data.shape)
print(data)
data_squ = data.squeeze()
print(data_squ.shape)
print(data_squ)
# 制定去除第二个1的维度
data_squ_1 = data.squeeze(2)
print(data_squ_1.shape)
print(data_squ_1)
print("-"*50)
# unsqueeze函数升维
data = torch.randint(1,10,[3,4,5])
print(data.shape)
# 参数表示升维的位置
data_unsqu = data.unsqueeze(-1)
print(data_unsqu.shape)
test2()torch.Size([1, 3, 1, 5]) tensor([[[[3, 4, 3, 8, 1]],
[[6, 7, 2, 8, 8]],
[[9, 1, 8, 8, 8]]]]) torch.Size([3, 5]) tensor([[3, 4, 3, 8, 1], [6, 7, 2, 8, 8], [9, 1, 8, 8, 8]]) torch.Size([1, 3, 5]) tensor([[[3, 4, 3, 8, 1], [6, 7, 2, 8, 8], [9, 1, 8, 8, 8]]]) -------------------------------------------------- torch.Size([3, 4, 5]) torch.Size([3, 4, 5, 1])
七、运算函数#
运算规则和numpy类似
def test0():
torch.random.manual_seed(42)
data1 = torch.randint(0,5,[2,3],dtype=torch.float64)
print(data1)
data2 = torch.randint(5,10,[2,3],dtype=torch.float64)
# 均值,默认对所有数据计算均值
print(data1.mean())
# 按照指定维度
print(data1.mean(dim=1))# 行
print(data1.mean(dim=0))# 列
# 求和sum()
# 平方pow()
# 平方根sqrt()
# 指数幂exp()
# 对数log2()
test0()tensor([[2., 2., 1.], [4., 1., 0.]], dtype=torch.float64) tensor(1.6667, dtype=torch.float64) tensor([1.6667, 1.6667], dtype=torch.float64) tensor([3.0000, 1.5000, 0.5000], dtype=torch.float64)
八、反向传播#
8.1 梯度基本计算#
8.1.1 标量梯度计算#
对于需要求导的张量需要设置参数为True,并且类型指定为小数
def test0():
x = torch.tensor(10,requires_grad=True,dtype=torch.float64)
f = x**2+20
# backward进行自动微分,2*x
f.backward()
# grad访问梯度
print(x.grad)
test0()tensor(20., dtype=torch.float64)
8.1.2 向量梯度计算#
上面f计算出来的是一个向量,不能直接求梯度,梯度必须是在标量基础上的
用均值或求和来表示f使f变成标量
def test1():
x = torch.tensor([10,20,30,40],requires_grad=True,dtype=torch.float64)
f = x**2+20
print("f",f)
f_mean = f.mean() # f_mean = f/4
f_mean.backward()
# grad访问梯度
print(x.grad)
test1()f tensor([ 120., 420., 920., 1620.], dtype=torch.float64,grad_fn=
8.1.3 多标量梯度计算#
多标量其实大差不差,只是最后要分开访问变量的梯度值就行了
def test2():
x1 = torch.tensor(10,requires_grad=True,dtype=torch.float64)
x2 = torch.tensor(20,requires_grad=True,dtype=torch.float64)
f = x1**2+x2**2+2*x1
print("f",f)
f.backward()
# grad分别访问x1,x2梯度
print(x1.grad)
print(x2.grad)
test2()f tensor(520., dtype=torch.float64, grad_fn=
8.1.4 多向量梯度计算#
def test3():
x1 = torch.tensor([10,20,30,40],requires_grad=True,dtype=torch.float64)
x2 = torch.tensor([40,50,60,70],requires_grad=True,dtype=torch.float64)
f = x1**2+x2**2+2*x1
print("f",f)
# 转为标量
f_mean = f.sum()
f_mean.backward()
# grad分别访问x1,x2梯度
print(x1.grad)
print(x2.grad)
test3()f tensor([1720., 2940., 4560., 6580.], dtype=torch.float64,
grad_fn=
8.2 控制梯度计算#
8.2.1 控制梯度计算
可以通过控制梯度计算,来设定忽略某个变量的梯度,设定后计算梯度将自动跳过
第一种方式:
def test4():
x = torch.tensor(10,requires_grad=True,dtype=torch.float64)
# 此时可以调用backward计算f的梯度
print(x.requires_grad)
with torch.no_grad():
f = x**2
print(f.requires_grad)
test4()True False
第二种方式:
@torch.no_grad()
def my_func(x):
return x**2
f = my_func(x)
print(f.requires_grad)False
第三种方式:全局禁用梯度
由于 f 是在禁用梯度计算的上下文中生成的,因此会抛出错误
torch.set_grad_enabled(False)
f = x**2
print(f.requires_grad)
f.backward()
# grad访问梯度
print(x.grad)8.2.2 累计梯度和梯度清零#
通过循环重复对x进行计算会将历史梯度值累加到x.grad属性中,相当于通过循环多次求导
def test5():
x = torch.tensor([10,20,30,40],requires_grad=True,dtype=torch.float32)
for _ in range(3):
f1 = x**2+20
# 将向量转换为标量
f2 = f1.mean()
# 自动微分
f2.backward()
print(x.grad)
test5() tensor([ 5., 10., 15., 20.]) tensor([10., 20., 30., 40.]) tensor([15., 30., 45., 60.])
但是在实际的梯度下降求解最优参数中,我们不希望这样的累加行为来影响梯度求解的结果,而是每次循环有不一样的数据来对损失函数进行优化,因此每次循环要进行梯度清零
def test6():
x = torch.tensor([10,20,30,40],requires_grad=True,dtype=torch.float32)
for _ in range(3):
f1 = x**2+20
# 将向量转换为标量
f2 = f1.mean()
# 梯度清零
if x.grad is not None:
x.grad.data.zero_()
# 自动微分
f2.backward()
print(x.grad)
test6() 在PyTorch中,梯度是累加的。这意味着每次调用 .backward() 时,计算得到的梯度会被累加到现有的梯度上,而不是替换它。如果不进行梯度清零,历史梯度会不断累积,导致梯度值变得非常大,这会影响模型的训练效果。
8.2.3 梯度下降优化算法案例#
在梯度清零的基础上,我们需要更新每次循环后的参数
def test7():
# 初始化一个x和theta
x = torch.tensor(10,requires_grad=True,dtype=torch.float64)
theta = 0.01
for i in range(1000):
# 前向传播
y = x**3
# 梯度清零
if x.grad is not None:
x.grad.data.zero_()
# 自动微分
y.backward()
# 更新参数
x.data = x.data-theta*x.grad
# 打印x值
print(x.data)
test7()
8.3 梯度计算注意#
detach() 方法用于将张量从计算图中分离出来,返回一个新的张量,该张量与原始张量共享数据,但不会参与梯度计算。它的主要用途包括防止梯度传播、节省内存和优化模型推理。
8.3.1 简单演示#
def test8():
x = torch.tensor([10,20],requires_grad=True,dtype=torch.float64)
# 演示上述错误
# print(x.numpy())
# 演示正确操作
print(x.detach().numpy())
test8()8.3.2 解决方案:detach()数据共享#
解释:
def test9():
# 演示正确操作
x1 = torch.tensor([10,20],requires_grad=True,dtype=torch.float64)
x2 = x1.detach()
print(id(x1.data),id(x2.data))
x2[0] = 100
print(x1)
test9()1607428373376 1607428373376 tensor([100., 20.], dtype=torch.float64, requires_grad=True)
九、基础组件#
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import make_regression
import matplotlib.pyplot as plt
from torch.utils.data import Dataset,DataLoader,TensorDataset9.1 基础组件的用法#
9.1.1 损失函数#
def test0():
# 初始化平方损失函数对象
criterion = nn.MSELoss()
# 该对象可以当做函数来使用
torch.random.manual_seed(42)
y_pred = torch.randn(3,5,requires_grad=True)
y_true = torch.randn(3,5,requires_grad=True)
# 计算损失
loss = criterion(y_pred,y_true)
print(loss)
test0()tensor(1.0192, grad_fn=
9.1.2 线性假设函数#
def test1():
# 输入数据特征为10,输出数据特征为5
model = nn.Linear(in_features=10,out_features=5)
# 构建4行10列输入数据
input = torch.randn(4,10)
y_pred = model(input)
print(y_pred.shape)
print(input)
print(y_pred)
test1()torch.Size([4, 5])
tensor([[ 0.4771, 0.7262, 0.0912, -0.3891, 0.5279, 1.0311, -0.7048, 1.0131,
0.7642, 1.0950],
[ 0.3399, 0.7200, 0.4114, -0.5733, 0.5069, -0.4752, -1.1299, -0.1360,
1.6354, 0.6547],
[ 0.5760, -0.3609, -0.0606, 0.0733, 0.4976, -0.4257, -1.3371, -0.1933,
0.6526, -1.9006],
[ 0.2286, 0.0249, 0.1947, -1.6535, 0.6814, 1.4611, -0.3098, -1.6022,
1.3529, 1.2888]])
tensor([[-0.5221, 0.6345, -0.4408, -1.4161, -0.7894],
[-0.3523, 1.0458, -0.2068, -1.1083, -0.2492],
[ 0.3888, -0.0686, -0.2927, -0.1234, 0.3353],
[ 0.2049, 0.3954, 0.5659, -1.4681, -0.4581]],
grad_fn=
9.1.3 优化器#
def test2():
model = nn.Linear(in_features=10,out_features=5)
# 传入模型参数和学习率
optimizer = optim.SGD(model.parameters(),lr=0.01)
# 此处省略backword
# 再调用backword之前要梯度清零
optimizer.zero_grad()
# 更新模型参数
optimizer.step()9.2 数据加载器的使用#
9.2.1 数据类创建#
如果是引用数据集,那么就自带有这个部分内容
class SampleDataset(Dataset):
def __init__(self,x,y):
# 初始化
self.x = x
self.y = y
self.len = len(y)
def __len__(self):
# 返回数据总量
return self.len
def __getitem__(self, index):
# 根据索引返回一条样本
# 将index限定在合理范围内
index = min(max(index,0),self.len-1)
return self.x[index],self.y[index]9.2.2 实例化数据加载类#
def test0():
x = torch.randn(100,8)
print(x.size())
y = torch.randint(0,2,(x.size(0),))
print(y)
sample_dataset = SampleDataset(x,y)
print(sample_dataset[0][0])
print(sample_dataset[0][1])
test0()torch.Size([100, 8]) tensor([0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]) tensor([ 1.1179, -1.2956, 0.0503, -0.5855, -0.3900, 0.0358, 0.1206, -0.8057]) tensor(0)
9.2.3 数据加载类的使用#
一次加载多条数据
def test1():
# 构建数据对象
x = torch.randn(100,8)
y = torch.randint(0,2,(x.size(0),))
sample_dataset = SampleDataset(x,y)
# 使用Dataloader
dataloader = DataLoader(sample_dataset,
batch_size=4,
shuffle=True)
for x,y in dataloader:
print(x)
print(y)
break
test1()tensor([[-0.5414, -1.0563, 0.2413, 0.1828, 0.6247, -0.7940, -0.6748, -0.3877], [ 0.6420, -0.8497, -0.6987, -0.2052, -0.7812, 0.6873, 0.7836, -1.1109], [-0.3359, -0.9029, 0.6440, 0.7592, -2.0203, -0.6740, -0.9192, 1.2120], [-1.2018, -0.5615, -0.9465, -0.7420, 0.1556, -0.2584, -0.7502, 1.2355]]) tensor([0, 0, 0, 0])
9.3 构建简单数据类型#
def test2():
# 构建数据对象
x = torch.randn(100,8)
y = torch.randint(0,2,(x.size(0),))
# 使用TensorDataset也可以来构建简单的数据类,就不需要上面的class类了
sample_dataset = TensorDataset(x,y)
# 使用Dataloader
dataloader = DataLoader(sample_dataset,
batch_size=4,
shuffle=True)
for x,y in dataloader:
print(x)
print(y)
break
test2()9.4 使用组件创建线性回归#
9.4.1 构建数据集#
def create_dataset():
x, y, coef = make_regression(n_samples=100,
n_features=1,
noise=10,
coef=True,
bias=14.5,
random_state=42)
# 将构建的数据转换为张量类型
x = torch.tensor(x, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32).view(-1, 1) # 转换为列向量
return x, y, coef9.4.2 创建训练函数以及训练结果可视化#
def train():
# 创建数据集
x, y, coef = create_dataset()
# 构建数据对象
sample_dataset = TensorDataset(x, y)
# 使用Dataloader
dataloader = DataLoader(sample_dataset,
batch_size=16,
shuffle=True)
# 构建线性模型
model = nn.Linear(in_features=1, out_features=1)
# 构建损失函数
criterion = nn.MSELoss()
# 优化方法
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 初始化训练参数
epochs = 1000
# 记录每个epoch的损失
epoch_losses = []
for epoch in range(epochs):
epoch_loss = 0.0
batch_count = 0
for train_x, train_y in dataloader:
# 将一个batch的数据输入模型
y_pred = model(train_x)
# 计算损失
loss = criterion(y_pred, train_y.reshape(-1, 1))
# 梯度清零
optimizer.zero_grad()
# 反向传播
loss.backward()
# 更新参数
optimizer.step()
# 累加损失
epoch_loss += loss.item()
batch_count += 1
# 计算平均损失
avg_loss = epoch_loss / batch_count
epoch_losses.append(avg_loss)
# 每10个epoch打印一次损失
if (epoch + 1) % 10 == 0:
print(f'Epoch [{epoch+1}/{epochs}], Loss: {avg_loss:.4f}')
# ==== 绘制拟合曲线 ====
plt.figure(figsize=(12, 5))
# 子图1:原始数据和拟合线
plt.subplot(1, 2, 1)
# 绘制原始数据点
plt.scatter(x.numpy(), y.numpy(), alpha=0.6, label='Original Data')
# 生成测试数据用于绘制拟合线
x_test = torch.linspace(x.min(), x.max(), 100).reshape(-1, 1)
with torch.no_grad(): # 禁用梯度计算
y_pred = model(x_test)
# 绘制拟合线
plt.plot(x_test.numpy(), y_pred.numpy(), 'r-', linewidth=2, label='Fitted Line')
# 获取模型参数
w, b = model.weight.item(), model.bias.item()
plt.title(f'Linear Regression: y = {w:.2f}x + {b:.2f}')
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True)
plt.legend()
# ==== 绘制损失曲线 ====
plt.subplot(1, 2, 2)
plt.plot(range(1, epochs + 1), epoch_losses, 'b-')
plt.title('Training Loss Curve')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.grid(True)
plt.tight_layout()
plt.show()
# 打印最终参数
print(f'\nTraining completed!')
print(f'Final parameters: weight = {w:.4f}, bias = {b:.4f}')
# 调用训练函数
train()