张量 - 线性回归 - 自动求导 - 逻辑回归
来源:投稿 来源:阿克西
编辑:学姐
4 张量操作
4.1 拼接
torch.cat()
torch.cat(tensors,
dim=0,
out=None)
功能:将张量按维度dim进行拼接,不会扩充张量的维度。
● tensors:张量序列
● dim:要拼接的维度
示例:
t = torch.ones((2, 3))
q = torch.zeros((2, 3))
t0 = torch.cat([t, q], dim=0) # 第0维度,2+2
t1 = torch.cat((t, q, t), dim=1) # 第1维度,3+3+3
print(t0, t0.shape)
print(t1, t1.shape)
tensor([[1., 1., 1.],
[1., 1., 1.],
[0., 0., 0.],
[0., 0., 0.]]) torch.Size([4, 3])
tensor([[1., 1., 1., 0., 0., 0., 1., 1., 1.],
[1., 1., 1., 0., 0., 0., 1., 1., 1.]]) torch.Size([2, 9])
torch.stack()
torch.stack(tensors,
dim=0,
out=None)
功能:在新创建的维度dim上进行拼接,会扩充张量的维度。
● tensors:张量序列
● dim:要拼接的维度
t = torch.ones((3, 4))
q = torch.zeros((3, 4))
print(t, "\n", q)
t0 = torch.stack([t, q], dim=0)
t1 = torch.stack([t, q], dim=1)
t2 = torch.stack([t, q], dim=2)
print(t0, "\n", t0.shape)
print(t1, "\n", t1.shape)
print(t2, "\n", t2.shape)
tensor([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
tensor([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
# 0维拼接
tensor([[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]],
[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]]])
torch.Size([2, 3, 4])
# 1维拼接
tensor([[[1., 1., 1., 1.],
[0., 0., 0., 0.]],
[[1., 1., 1., 1.],
[0., 0., 0., 0.]],
[[1., 1., 1., 1.],
[0., 0., 0., 0.]]])
torch.Size([3, 2, 4])
# 2维拼接
tensor([[[1., 0.],
[1., 0.],
[1., 0.],
[1., 0.]],
[[1., 0.],
[1., 0.],
[1., 0.],
[1., 0.]],
[[1., 0.],
[1., 0.],
[1., 0.],
[1., 0.]]])
torch.Size([3, 4, 2])
4.2 切分
torch.chunk()
torch.chunk(input,
chunks,
dim=0)
功能:将张量按维度dim进行平均切分。
返回值:张量列表。
注意事项:若不能整除,最后一份张量小于其他张量。
● input:要切分的张量
● chunks:要切分的份数
● dim:要切分的维度
示例:
t = torch.ones((2, 7))
list_t = torch.chunk(t, chunks=3, dim=1)
for idx, ten in enumerate(list_t):
print("第{}个张量:\n{}".format(idx+1, ten), ten.shape)
向上取整:
第1个张量:
tensor([[1., 1., 1.],
[1., 1., 1.]]) torch.Size([2, 3])
第2个张量:
tensor([[1., 1., 1.],
[1., 1., 1.]]) torch.Size([2, 3])
第3个张量:
tensor([[1.],
[1.]]) torch.Size([2, 1])
torch.split()
torch.split(tensor,
split_size_or_sections,
dim=0)
功能:将张量按维度dim进行切分。
返回值:张量列表。
● tensor:要切分的张量
● split_size_or_sections:为int时,表示每一份的长度;为list时,按list元素切分
● dim:要切分的维度
t = torch.ones((2, 7))
list_t = torch.split(t, 3, dim=1)
for i, ten in enumerate(list_t):
print("第{}个张量:\n{}".format(i+1, ten), ten.shape)
print("\n")
list_t = torch.split(t, [3, 4], dim=1)
for i, ten in enumerate(list_t):
print("第{}个张量:\n{}".format(i+1, ten), ten.shape)
第1个张量:
tensor([[1., 1., 1.],
[1., 1., 1.]]) torch.Size([2, 3])
第2个张量:
tensor([[1., 1., 1.],
[1., 1., 1.]]) torch.Size([2, 3])
第3个张量:
tensor([[1.],
[1.]]) torch.Size([2, 1])
第1个张量:
tensor([[1., 1., 1.],
[1., 1., 1.]]) torch.Size([2, 3])
第2个张量:
tensor([[1., 1., 1., 1.],
[1., 1., 1., 1.]]) torch.Size([2, 4])
4.3 比较
torch.ge(),torch.gt(),torch.le(),torch.lt()
torch.ge(input,
other,
out=None)
功能:input中逐元素与other进行比较,满足:ge >=; gt >; le <=; lt <时,返回True。
返回值:与input同形状的布尔类型张量。
● input:被比较的张量
● other:可以是张量,数值,布尔,input中逐元素与其进行比较
示例:
t = torch.randint(0, 10, [3, 3])
m = t.ge(5)
print(t)
print(m)
tensor([[1, 6, 5],
[6, 5, 4],
[0, 4, 4]])
tensor([[False, True, True],
[ True, True, False],
[False, False, False]])
4.4 索引
torch.index_select()
torch.index_select(input,
dim,
index,
out=None)
功能:在维度dim上,按index索引数据。
返回值:索引得到的数据拼接的张量。
● input:要索引的张量
● dim:要索引的维度
● index:要索引数据的序号组成的张量,dtype须为torch.long
示例:
t = torch.randint(0, 10, [3, 3])
idx = torch.tensor([0, 2], dtype=torch.long)
sel = torch.index_select(t, dim=0, index=idx)
print("t: \n", t)
print("idx: \n", idx)
print("sel: \n", sel)
t:
tensor([[9, 6, 9],
[9, 3, 7],
[9, 9, 7]])
idx:
tensor([0, 2])
sel:
tensor([[9, 6, 9],
[9, 9, 7]])
torch.masked_select()
torch.masked_select(input,
mask,
out=None)
功能:按mask中的True进行索引,常用来筛选数据。
返回值:一维张量,符合要求的所有数值。
● input:要索引的张量
● mask:与input同形状的布尔类型张量
t = torch.randint(0, 9, [3, 3])
mask = t.ge(5)
sel = torch.masked_select(t, mask)
print("t: \n", t)
print("mask: \n", mask)
print("sel: \n", sel)
t:
tensor([[1, 2, 3],
[3, 5, 3],
[7, 3, 8]])
mask:
tensor([[False, False, False],
[False, True, False],
[ True, False, True]])
sel:
tensor([5, 7, 8])
4.5 变换
torch.reshape()
torch.reshape(input,
shape)
功能:变换张量形状。
注意事项:当张量在内存中是连续的时,新张量与input共享数据内存。这种共享与out不同,out是整个tensor都共享内存,相当于别名;reshape是仅data共享内存。改变一个张量的数据,另一个张量会跟着改变。
● input:要变换的张量
● shape:新张量的形状,新张量与原张量乘积相等
t = torch.randperm(8)
re1 = torch.reshape(t, (2, 4))
re2 = torch.reshape(t, (-1, 4))
print("t: \n", t)
print("re1: \n", re1)
print("re2: \n", re2)
t[0] = 100
re2[1, 1] = 100
print(id(t.data), id(re1.data), id(re2.data))
print("re1: \n", re1)
t:
tensor([7, 5, 4, 3, 6, 2, 0, 1])
re1:
tensor([[7, 5, 4, 3],
[6, 2, 0, 1]])
re2:
tensor([[7, 5, 4, 3],
[6, 2, 0, 1]])
139962284382720 139962284382720 139962284382720
re1:
tensor([[100, 5, 4, 3],
[ 6, 100, 0, 1]])
-1表示不需要关心该维度,根据其他维度计算。
torch.transpose()
torch.transpose(input,
dim0,
dim1)
功能:交换张量的两个维度。在图像的预处理中常用,有时读取的图像数据是 (c, h, w),但是我们常用的是 (h, w, c),就需要用此方法把channel和width变换,再把width和height变换。
● input:要变换的张量
● dim0:要交换的维度
● dim1:要交换的维度
示例:
t = torch.rand((2, 3, 4))
tr = torch.transpose(t, dim0=0, dim1=1)
print(t.shape)
print(tr.shape)
torch.Size([2, 3, 4])
torch.Size([3, 2, 4])
torch.t()
torch.t(input)
功能:2维张量转置,对矩阵而言,等价于torch.transpose(input, 0, 1)。
torch.squeeze()
torch.squeeze(input,
dim=None,
out=None)
功能:压缩长度为1的维度(轴)。
● dim:若为None,移除所有长度为1的轴;若指定维度,当且仅当该轴长度为1时,可以被移除。
示例:
t = torch.rand((1, 2, 3, 1))
sq = torch.squeeze(t)
sq0 = torch.squeeze(t, 0)
sq1 = torch.squeeze(t, 1)
print(t.shape)
print(sq.shape)
print(sq0.shape)
print(sq1.shape)
torch.Size([1, 2, 3, 1])
torch.Size([2, 3])
torch.Size([2, 3, 1])
torch.Size([1, 2, 3, 1])
torch.unsqueeze()
torch.usqueeze(input,
dim,
out=None)
功能:依据dim扩展维度。
● dim:扩展的维度
示例:
t = torch.rand((2, 3))
sq = torch.unsqueeze(t, 0)
print(t.shape)
print(sq.shape)
torch.Size([2, 3])
torch.Size([1, 2, 3])
4.6 数学运算
加减乘除
torch.add(input, alpha=1, other, out=None)
torch.addcdiv(tensor, value=1, tensor1, tensor2, out=None)
torch.addcmul(tensor, value=1, tensor1, tensor2, out=None)
torch.sub(input, other, out=None)
torch.mul(input, other, out=None)
torch.div(input, other, out=None)
对数,指数,幂函数
torch.log(input, out=None) # 以e为底
torch.log10(input, out=None)
torch.log2(input, out=None)
torch.exp(input, out=None)
torch.pow(input, exponent, out=None)
三角函数
torch.acos(input, out=None)
torch.cosh(input, out=None)
torch.cos(input, out=None)
torch.asin(input, out=None)
torch.sinh(input, out=None)
torch.sin(input, out=None)
torch.atan(input, out=None)
torch.tanh(input, out=None)
torch.tan(input, out=None)
torch.atan2(input, other, out=None)
绝对值
torch.abs(input, out=None)
torch.add()
torch.add(input,
alpha=1,
other,
out=None)
功能:逐元素计算,因为深度学习中经常用到先乘后加的操作,所以增加了此功能,可以使代码更加整洁。
● input:第一个张量
● alpha:乘项因子
● other:第二个张量
t_0 = torch.rand((3, 3))
t_1 = torch.ones_like(t_0)
t_add = torch.add(t_0, 10, t_1)
print(t_0)
print(t_1)
print(t_add)
tensor([[0.7670, 0.1988, 0.6214],
[0.6645, 0.5464, 0.8711],
[0.1995, 0.5937, 0.8941]])
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
tensor([[10.7670, 10.1988, 10.6214],
[10.6645, 10.5464, 10.8711],
[10.1995, 10.5937, 10.8941]])
torch.addcdiv()
torch.addcdiv(tensor,
value=1,
tensor1,
tensor2,
out=None)
功能:逐元素计算,优化过程中常用。
torch.addcmul()
torch.addcmul(tensor,
value=1,
tensor1,
tensor2,
out=None)
功能:逐元素计算,优化过程中常用。
5 线性回归
概念:线性回归是分析一个变量与另外一或多个变量之间关系的方法。
求解步骤:
LR为步长,学习率
import torch
import matplotlib.pyplot as plt
torch.manual_seed(10) # 初始化随机数种子,保证结果可以复现
lr = 0.01 # 学习率
best_loss = float("inf")
# 创建训练数据
x = torch.rand(200, 1) * 10
y = 3 * x + (5 + torch.randn(200, 1))
# 构建线性回归参数
w = torch.randn((1), requires_grad=True)
b = torch.zeros((1), requires_grad=True)
for iteration in range(10000):
# 前向传播
wx = torch.mul(w, x)
y_pred = torch.add(wx, b)
# 计算 MSE loss,乘以0.5是为了求导过程中消除平方2的影响,mean()求均值
loss = (0.5 * (y - y_pred) ** 2).mean()
# 反向传播
loss.backward() # 自动求导
current_loss = loss.item()
if current_loss < best_loss:
best_loss = current_loss
best_w = w
best_b = b
# 绘图
if iteration%100 == 0:
if loss.data.numpy() < 3:
plt.scatter(x.data.numpy(), y.data.numpy())
plt.plot(x.data.numpy(), y_pred.data.numpy(), 'r-', lw=5)
plt.text(2, 20, 'Loss=%.4f' % loss.data.numpy(), fontdict={'size': 20, 'color': 'red'})
plt.xlim(1.5, 10)
plt.ylim(8, 40)
plt.title("Iteration: {}\nw: {} b: {}".format(iteration, w.data.numpy(), b.data.numpy()))
plt.pause(0.5)
if loss.data.numpy() < 0.55:
break
# 更新参数
w.data.sub_(lr * w.grad)
b.data.sub_(lr * b.grad)
# 梯度清零
w.grad.zero_()
b.grad.zero_()
print(best_loss, best_w, best_b)
0.5460291504859924 tensor([3.0449], requires_grad=True) tensor([4.5874], requires_grad=True)
6 计算图与动态图机制
6.1 计算图
计算图是用来描述运算的有向无环图。计算图的好处是运算更加简洁,求导更加方便。
计算图有两个主要元素:结点(Node) 和边(Edge)。
● 结点表示数据,如向量,矩阵,张量;
● 边表示运算,如加减乘除卷积等。
代码验证:
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(x, w)
b = torch.add(w, 1)
y = torch.mul(a, b)
y.backward()
print(w.grad)
# 判断是否是叶子节点
print("is_leaf叶子节点:", w.is_leaf, x.is_leaf, a.is_leaf, b.is_leaf, y.is_leaf)
print("grad梯度:", w.grad, x.grad, a.grad, b.grad, y.grad)
tensor([5.])
is_leaf叶子节点: True True False False False
grad梯度: tensor([5.]) tensor([2.]) None None None
叶子结点:用户创建的结点,如上图中的X与W,梯度反向传播之后,非叶子节点的梯度会被释放掉,可以节省内存。若想使用非叶子节点的梯度,可以用 retain_grad()。
● torch.Tensor中的属性,is_leaf:指示张量是否为叶子结点
● grad_fn:记录创建该张量时所用的方法(函数),在梯度反向传播是用到
● y.grad_fn = MulBackward0,乘法得到
● a.grad_fn = AddBackward0,加法得到
● b.grad_fn = AddBackward0
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(x, w)
a.retain_grad() # 保留非叶子节点的梯度
b = torch.add(w, 1)
y = torch.mul(a, b)
y.backward()
# 判断是否是叶子节点
print("is_leaf叶子节点:", w.is_leaf, x.is_leaf, a.is_leaf, b.is_leaf, y.is_leaf)
print("grad梯度:", w.grad, x.grad, a.grad, b.grad, y.grad)
print(w.grad_fn, x.grad_fn, a.grad_fn, b.grad_fn, y.grad_fn)
is_leaf叶子节点: True True False False False
grad梯度: tensor([5.]) tensor([2.]) tensor([2.]) None None # a.grad不为None
None None <AddBackward0 object at 0x7fc2c1734d10> <AddBackward0 object at 0x7fc28d007050> <MulBackward0 object at 0x7fc28e38abd0>
6.2 动态图机制
动态图 vs 静态图
根据计算图搭建方式,可将计算图分为动态图和静态图
PyTorch采用的是动态图机制,每一次训练,都会销毁计算图并重新创建,这样做花销很大,但是更加灵活。
TensorFlow采用的是静态图机制,一旦定义,训练时就不能修改。一开始就要搭建好计算图,然后才能进行数据的传输和计算,这意味着写代码过程中,错误将更加难以发现,因为搭建图的过程中不会报错,所以不知道错在哪里,只有在传入数据的过程中才能发现哪里出错,灵活性低,而且出现bug也不易调试。
7 autograd - 自动求导系统
深度学习模型的训练,就是不断更新权值,权值的更新需要求解梯度,求解梯度十分繁琐,PyTorch提供自动求导系统,我们只要搭建好前向传播的计算图,就能获得所有张量的梯度。
7.1 torch.autograd.backward()
torch.autograd.backward(tensors,
grad_tensors=None,
retain_graph=None,
create_graph=False)
功能:自动求取梯度。
● tensors:用于求导的张量,如 loss
● retain_graph:保存计算图,由于PyTorch采用动态图机制,在每次反向传播之后计算图都会释放掉,如果还想继续使用,就要设置此参数为True
● create_graph:创建导数计算图,用于高阶求导
● grad_tensors:多梯度权重,当有多个loss需要计算梯度时,需要设置每个loss的权值
示例1:y.backward()与torch.autograd.backward()的关系
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(x, w)
b = torch.add(w, 1)
y = torch.mul(a, b)
y.backward()
print(w.grad)
tensor([5.])
调试:
在 y.backward() 处设置断点,step into进入函数
可以看到张量中的类方法只有一行,说明 y.backward() 直接调用了torch.autograd.backward()方法。
点击单步调试 step over 返回 y.backward(),停止调试。
示例2:retain_graph,保存计算图
多次执行y.backward()会报错,因为计算图被释放,解决方法是第一次反向传播时,设置y.backward(retain_graph=True)
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(x, w)
b = torch.add(w, 1)
y = torch.mul(a, b)
# 会报错
# y.backward()
# y.backward()
# 正确写法
y.backward(retain_graph=True)
y.backward() # 再执行一次反向传播
RuntimeError: Trying to backward through the graph a second time, but the buffers have already been freed. Specify retain_graph=True when calling backward the first time.
tensor([10.]) # 可以看出梯度进行了累加
示例3:grad_tensors,多梯度权重
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(w, x) # retain_grad()
b = torch.add(w, 1)
y0 = torch.mul(a, b) # y0 = (x+w) * (w+1) dy0/dw = 5
y1 = torch.add(a, b) # y1 = (x+w) + (w+1) dy1/dw = 2
loss = torch.cat([y0, y1], dim=0) # [y0, y1]
grad_tensors = torch.tensor([1., 2.])
# gradient 传入 torch.autograd.backward()中的grad_tensors
loss.backward(gradient=grad_tensors)
print(w.grad)
tensor([9.])
7.2 torch.autograd.grad()
torch.autograd.grad(outputs,
inputs,
grad_outputs=None,
retain_graph=None,
create_graph=False)
功能:求取梯度。
● outputs:用于求导的张量,如上例中的 loss
● inputs:需要梯度的张量,如上例中的w
● create_graph:创建导数计算图,用于高阶求导
● retain_graph:保存计算图
● grad_outputs:多梯度权重
示例:计算的二阶导数
x = torch.tensor([3.], requires_grad=True)
y = torch.pow(x, 2) # y = x**2
# create_graph=True 创建导数的计算图,实现高阶求导
grad_1 = torch.autograd.grad(y, x, create_graph=True)
# grad_1 = dy/dx = 2x = 2 * 3 = 6
print(grad_1)
grad_2 = torch.autograd.grad(grad_1[0], x)
# grad_2 = d(dy/dx)/dx = d(2x)/dx = 2
print(grad_2)
(tensor([6.], grad_fn=<MulBackward0>),)
(tensor([2.]),)
7.3 小贴士
1、梯度不自动清零,在每次反向传播中会叠加
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
for i in range(3):
a = torch.add(x, w)
b = torch.add(w, 1)
y = torch.mul(a, b)
y.backward()
print(w.grad)
tensor([5.])
tensor([10.])
tensor([15.])
这导致我们得不到正确的结果,所以需要手动清零
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
for i in range(3):
a = torch.add(x, w)
b = torch.add(w, 1)
y = torch.mul(a, b)
y.backward()
print(w.grad)
# 梯度清零
w.grad.zero_()
tensor([5.])
tensor([5.])
tensor([5.])
这里zero后接下划线_,表示原位(in-place)操作。
2、依赖于叶子结点的结点(如上例中的a和b),requires_grad默认为True,因为叶子结点梯度的计算与该节点有关
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(x, w)
b = torch.add(w, 1)
y = torch.mul(a, b)
print(a.requires_grad, b.requires_grad, y.requires_grad)
可以看出虽然只针对x和w设置了requires_grad,但a,b,y也均为True
True True True
3、叶子结点不可执行in-place,因为前向传播记录了叶子节点的地址,反向传播需要用到叶子节点的数据时,要根据地址寻找数据,执行in-place操作改变了地址中的数据,梯度求解也会发生错误。
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(x, w)
b = torch.add(w, 1)
y = torch.mul(a, b)
# 对叶子节点w加1,报错
w.add_(1)
RuntimeError: a leaf Variable that requires grad has been used in an in-place operation.
in-place操作,即原位操作,在原始内存中改变这个数据,方法后接_代表in-place操作
a = torch.tensor([1])
print(id(a), a)
# 开辟了新的内存地址
a = a + torch.tensor([1])
print(id(a), a)
# in-place操作,地址不变
a += torch.tensor([1])
print(id(a), a)
139712364650320 tensor([1])
139712364675232 tensor([2])
139712364675232 tensor([3])
8 逻辑回归
逻辑回归是线性的二分类模型。
x = torch.arange(-10, 10, 0.2)
y = torch.sigmoid(x)
plt.plot(x.data.numpy(), y.data.numpy(), lw=5)
plt.xlim(-10, 10)
plt.ylim(0, 1)
plt.vlines(0, 0, 1, linestyles="--", colors='gray')
plt.hlines(0.5, -10, 10, linestyles="--", colors='gray')
plt.show()
import torch
torch.manual_seed(10) # 初始化随机数种子,保证结果可以复现
from torch import nn
import matplotlib.pyplot as plt
import numpy as np
# ============================ step 1/5 生成数据 ============================
sample_nums = 100
mean_value = 1.7
bias = 1
n_data = torch.ones(sample_nums, 2)
# print(n_data)
# 均值的shape=(100, 2),方差均为标量1
x0 = torch.normal(mean_value * n_data, 1) + bias # 类别0 数据 shape=(100, 2)
y0 = torch.zeros(sample_nums) # 类别0 标签 shape=(100)
# print(x0)
x1 = torch.normal(-mean_value * n_data, 1) + bias # 类别1 数据 shape=(100, 2)
y1 = torch.ones(sample_nums) # 类别1 标签 shape=(100)
train_x = torch.cat((x0, x1), 0) # shape=(200, 2)
train_y = torch.cat((y0, y1), 0) # shape=(200)
print(train_x.shape)
# ============================ step 2/5 选择模型 ============================
class LR(nn.Module):
def __init__(self):
super(LR, self).__init__()
self.features = nn.Linear(2, 1)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
x = self.features(x)
x = self.sigmoid(x)
return x
lr_net = LR() # 实例化逻辑回归模型
# ============================ step 3/5 选择损失函数 ============================
loss_fn = nn.BCELoss()
# ============================ step 4/5 选择优化器 ============================
lr = 0.01 # 学习率
optimizer = torch.optim.SGD(lr_net.parameters(), lr=lr, momentum=0.9)
# ============================ step 5/5 模型训练 ============================
for iteration in range(1000):
# 前向传播
y_pred = lr_net(train_x)
# 计算 loss
loss = loss_fn(y_pred.squeeze(), train_y)
# 反向传播
loss.backward()
# 更新参数
optimizer.step()
# 清空梯度
optimizer.zero_grad()
# 绘图
if iteration % 20 == 0:
mask = y_pred.ge(0.5).float().squeeze() # 以0.5为阈值进行分类
correct = (mask == train_y).sum() # 计算正确预测的样本个数
acc = correct.item() / train_y.size(0) # 计算分类准确率
plt.scatter(x0.data.numpy()[:, 0], x0.data.numpy()[:, 1], c='r', label='class 0')
plt.scatter(x1.data.numpy()[:, 0], x1.data.numpy()[:, 1], c='b', label='class 1')
w0, w1 = lr_net.features.weight[0]
w0, w1 = float(w0.item()), float(w1.item())
plot_b = float(lr_net.features.bias[0].item())
plot_x = np.arange(-6, 6, 0.1)
plot_y = (-w0 * plot_x - plot_b) / w1
plt.xlim(-5, 7)
plt.ylim(-7, 7)
plt.plot(plot_x, plot_y)
plt.text(-5, 5, 'Loss=%.4f' % loss.data.numpy(), fontdict={'size': 20, 'color': 'red'})
plt.title("Iteration: {}\nw0:{:.2f} w1:{:.2f} b: {:.2f} accuracy:{:.2%}".format(iteration, w0, w1, plot_b, acc))
plt.legend()
plt.show()
plt.pause(0.5)
if acc > 0.99:
break
由图二可以看出,如果把mean调整得更小,例如1或者0.5,会发现样本点有部分重合,即使训练到最后,准确率也很低;如果把mean调整得更大,例如5,会发现样本点分布很明显,更容易分类。
由图三可以看出,如果把bias调整成绝对值很大的数,例如50,会发现不管怎么训练模型都无法分类,这是因为,从sigmoid图像中可以看出,如果数据比较大的时候,梯度几乎为0,反向传播求导无法求到一个很好的梯度,所以没有办法训练模型,这种情况称为梯度消失。
所以我们要对数据进行归一化处理,让它们处于0附近。
9 机器学习的训练步骤
9.1 数据
● 数据收集:收集原始样本和标签,如Img和Label。 ● 数据划分:划分成训练集train,用来训练模型;验证集valid,验证模型是否过拟合,挑选还没有过拟合的时候的模型;测试集test,测试挑选出来的模型的性能。 ● 数据读取:PyTorch中数据读取的核心是Dataloader。Dataloader分为Sampler和DataSet两个子模块。Sampler的功能是生成索引,即样本序号;DataSet的功能是根据索引读取样本和标签。 ● 数据预处理:数据的中心和,标准化,旋转,翻转等,在PyTorch中是通过transforms实现的。
9.2 选择模型
根据任务的难易程度选择简单的线性模型或者复杂的神经网络模型。
9.3 损失函数
根据不同的任务选择不同的损失函数,比如线性回归中选择均方差损失函数,分类选择交叉熵。
9.4 优化器
有了loss就可以求取梯度,得到梯度,用优化器更新权值。
9.5 迭代训练
反复训练的过程
点击下方卡片《学姐带你玩AI》🚀🚀🚀
关注回复“500”领取300+经典论文合集&讲解视频
码字不易,欢迎大家点赞评论收藏!