第一章 人工神经网络基础
1.1 人工智能与传统机器学习
学习心得:
传统机器学习(ML):需要专业的主题专家人工提取特征,并通过一个编写良好的算法来破译给定的特征,从而判断这幅图像中的内容。
输入-->人工提取特征-->特征-->具有浅层结构的分类器-->输出
当存在欺骗性的图片出现时可能会失效,我们需要创建能够精细分类多种类型的人为规则数量可能是指数级的,,因此传统方法在非常受限的环境中很有效。
我们可以将同样的思路扩展到任何领域比如:文本或结构化数据,在过去如果想通过编程解决现实世界的任务,那么必须理解关于输入数据的所有内容,并且需要编写尽可能多的规则来覆盖每个场景,不仅乏味,而且不能保证所有的新场景都会遵循上述规则。
然而,通过人工神经网络,神经网络提供了特征提取和决策分类的模块,几乎不需要手工提取特征,只需要标记数据(如:哪些是人,哪些不是人)和神经网络架构,这样就消除了传统技术(如何创造规则)带来的负担
输入-->特征学习+分类器(端到端选择)-->输出
但是在这里,我们需要提供大量样本,如:大量人和不含人的图片输入给模型,以便它学习特征
1.2 人工神经网络的构建模块
可以将人工神经网络看作一个数学函数,输入一个或者多个张量(权重),输出一个或者多个张量(权重)连接这些输入和输出的运算排列称为神经网络的架构,我们可以根据手头任务定制。
输入层:自变量的输入。
隐藏(中间)层:连接输入和输出层,并对输入数据进行转换,此外隐藏层包含节点, 用于将输入值修改为更高/更低维度的值,可以通过修改中间层节点的激活函数实现更复杂的表示。
输出层:输入变量后产生期望的值,树输出层节点数量取决于手头任务,及试图预测的是连续还是分类变量,若是连续的则输出层只有一个节点,若是n个分类则输出层就有n个节点。
激活函数:
深度学习指的是具有更多隐藏层
1.3 前向传播
在开始前使用随机权重初始化
-
计算隐藏层的值
-
进行非线性激活
sigmoid = 1/(1 + e^-x)
ReLU = x if x > 0 else 0
tanh = (e^x - e-x)/(ex + e^-x) -
估算输出层的值
-
计算与期望值对应的损失值
计算连续变量的损失一般用MSE(均方误差的平方可以保证正误差和负误差不相互抵消)
np.mean(np.square(pred - y))计算分类变量的损失一般用二元交叉熵损失(当只有两个类别的时候)
-np.mean((y*np.log(p))+(1-y)*np.log(1-p))
一般多分类用分类交叉熵
(图)
数据集的最终损失是所有单个损失的平均值。
补:平均绝对误差
np.mean(np.abs(pred - y))
# 在预测输出小于1的时候,使用均方误差可以大大降低损失量
np.mean(np.square(pred - y))
前向传播(Numpy)
def feed_forward(inputs, outputs, weights):
pre_hidden = np.dot(inputs,weights[0])+ weights[1]
hidden = 1/(1+np.exp(-pre_hidden))
pred_out = np.dot(hidden, weights[2]) + weights[3]
mean_squared_error = np.mean(np.square(pred_out - outputs))
return mean_squared_error
1.4 反向传播
通过更新权重来减小误差的过程称为梯度下降
Numpy实现每次对一个参数少量更新,实现梯度下降
from copy import deepcopy
import numpy as np
import matplotlib.pyplot as plt
# 更新权重
def update_weights(inputs, outputs, weights, lr):
original_weights = deepcopy(weights)
updated_weights = deepcopy(weights)
original_loss = feed_forward(inputs, outputs, original_weights)
# 先计算一次损失
for i, layer in enumerate(original_weights):
for index, weight in np.ndenumerate(layer):
temp_weights = deepcopy(weights)
temp_weights[i][index] += 0.0001
# 每次将权重和偏置的一个增加很小的量
_loss_plus = feed_forward(inputs, outputs, temp_weights)
# 再计算一次损失
grad = (_loss_plus - original_loss)/(0.0001)
# 计算梯度(参数值改变引起的损失的改变)
updated_weights[i][index] -= grad*lr
# 更新参数,使用学习率慢慢建立信任
return updated_weights, original_loss
# 输入输出
x = np.array([[1,1]])
y = np.array([[0]])
# 参数随机初始化
W = [
np.array([[-0.0053, 0.3793],
[-0.5820, -0.5204],
[-0.2723, 0.1896]], dtype=np.float32).T,
np.array([-0.0140, 0.5607, -0.0628], dtype=np.float32),
np.array([[ 0.1528, -0.1745, -0.1135]], dtype=np.float32).T,
np.array([-0.5516], dtype=np.float32)
]
# 绘图
losses = []
for epoch in range(100):
W, loss = update_weights(x,y,W,0.01)
losses.append(loss)
plt.plot(losses)
plt.title('Loss over increasing number of epochs')
批大小:当有成千上万的数据的时候,如果一起输入到网络计算损失收益不是很高,因此使用批大小进行模型训练
1.5 链式法则的反向传播
利用偏导数就可以仅仅通过利用前向传播计算出的损失来更新参数。
1.6 学习率的影响
当学习率很小的时候,权值向最优值的移动比较慢。
当学习率稍大的时候,权值先是震荡,然后快速向最优值收敛。
当学习率很大的时候,权值会达到一个很大的值,无法达到最优值。
(权值小于最优值的时候梯度为负,反之梯度为正)
一般来说学习率越小越好(0.0001-0.01)
第二章 Pytorch基础
2.1 Pytorch 张量
学习心得:
标量是0维张量
向量可以表示一维张量(轴0)
形状(4,)
二维矩阵表示二维张量(上到下轴0,左到右轴1)
形状(4,3)
三维维矩阵表示三维张量(上到下轴0,左到右轴1,外到内轴2)
形状(4,3,2)
初始化张量
import torch
x = torch.tensor([[1,2]])
y = torch.tensor([[1],[2]])
print(x.shape)
# torch.Size([1,2]) # one entity of two items
print(y.shape)
# torch.Size([2,1]) # two entities of one item each
torch.zeros(3, 4)
# tensor([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
torch.ones(3, 4)
# tensor([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
torch.randint(low=0, high=10, size=(3,4))
# tensor([[8, 2, 5, 9],
[6, 1, 6, 0],
[5, 6, 9, 5]])
torch.rand(3, 4) # 0-1之间随机
# tensor([[0.3196, 0.9387, 0.9268, 0.1246],
[0.6700, 0.7529, 0.8687, 0.3948],
[0.2279, 0.2309, 0.0151, 0.0339]])
torch.randn(3,4) # 服从正态分布的随机
# tensor([[-0.4039, -1.8015, 0.9784, -1.5263],
[ 0.9577, -1.2826, 0.2746, -0.2621],
[-1.4713, 0.6437, 0.3326, -1.0703]])
x = np.array([[10,20,30],[2,3,4]])
# np.ndarrary
y = torch.tensor(x)
# 将numpy转换为张量
print(type(x), type(y))
# <class 'numpy.ndarray'> <class 'torch.Tensor'>
张量运算
x = torch.tensor([[1,2,3,4], [5,6,7,8]])
print(x * 10)
# tensor([[10, 20, 30, 40],
# [50, 60, 70, 80]])
x = torch.tensor([[1,2,3,4], [5,6,7,8]])
y = x.add(10)
print(y)
#tensor([[11, 12, 13, 14],
# [15, 16, 17, 18]])
重塑张量
y = torch.tensor([2, 3, 1, 0])
y = y.view(4,1)
y
# tensor([[2],
[3],
[1],
[0]])
#另一种重塑方法squeeze方法,只适合在某个轴上数值为1才行
x = torch.randn(10,1,10)# 三维张量轴0,轴1 轴2
z1 = torch.squeeze(x, 1) # 1表示轴为1
# z1 = x.squeeze(1)
z1.shape
# torch.Size([10, 10])
x = torch.randn(10,1,10)# 三维张量轴0,轴1 轴2
z1 = torch.unsqueeze(x, 0) # 1表示轴为1
# z1 = x.unsqueeze(0)
# torch.Size([1,10,10])
除了unsqueeze也可以用None见下
z2, z3, z4 = x[None,:,:], x[:,None,:], x[:,:,None]
# torch.Size([1, 10, 10])
torch.Size([10, 1, 10])
torch.Size([10, 10, 1])
张量的矩阵乘法
y = torch.tensor([2, 3, 1, 0])
x = torch.tensor([[1,2,3,4], [5,6,7,8]])
print(torch.matmul(x, y))
# 或者 print(x@y)
张量的连接
x = torch.randn(10,10,10)
z = torch.cat([x,x], axis=0) # np.concatenate()
print('Cat axis 0:', z.shape)
# torch.Size([20, 10, 10])
提取张量最大值
x = torch.arange(25).reshape(5,5)
print('Max:', x.shape, x.max())
# Max: torch.Size([5, 5]) tensor(24)
x.max(dim=0)# 轴0上的最大值
# torch.return_types.max(
values=tensor([20, 21, 22, 23, 24]),
indices=tensor([4, 4, 4, 4, 4]))
x.max(dim=1)# 轴1上的最大值
# torch.return_types.max(
values=tensor([ 4, 9, 14, 19, 24]),
indices=tensor([4, 4, 4, 4, 4]))
置换张量维数(不要通过重塑张量来交换维数)
x = torch.randn(10,20,30)# (0,1,2)
z = x.permute(2,0,1) # np.permute()
print('Permute dimensions:',z.shape)
# torch.Size([30, 10, 20])
dir(torch.Tensor)
查看张量方法
help(torch.Tensor.<method>)
对某个方法查看如何使用
2.2 张量的自动梯度
x = torch.tensor([[2., -1.], [1., 1.]], requires_grad=True)
# requires_grad=True 参数指定为张量对象计算梯度
# tensor([[ 2., -1.],
[ 1., 1.]], requires_grad=True)
out = x.pow(2).sum()
out.backward()
# 计算out的梯度
x.grad
# 得到out 关于 x 的梯度
# tensor([[ 4., -2.],
[ 2., 2.]])
一般来说,在CPU上使用Torch张量运算仍然比Numpy要快
torch的GPU最快
2.3 Pytorch构建神经网络
import torch
x = [[1,2],[3,4],[5,6],[7,8]]
y = [[3],[7],[11],[15]]
# 转换为浮点对象
X = torch.tensor(x).float()
Y = torch.tensor(y).float()
print(X,Y)
# 将数据注册到GPU
device = 'cuda' if torch.cuda.is_available() else 'cpu'
X = X.to(device)
Y = Y.to(device)
# 定义网络架构
import torch.nn as nn
# 对nn.Module的继承是强制的因为是神经网络的基类
# 必须利用super().__init__()来确保继承nn.Module就可以利用事先编写好的功能
class MyNeuralNet(nn.Module):
def __init__(self):
super().__init__()
self.input_to_hidden_layer = nn.Linear(2,8)#等价于 nn.Parameter(torch.rand(2,8))
# 具体为Linear(in_features = 2 , out_features = 8 , bias = True)
self.hidden_layer_activation = nn.ReLU()
self.hidden_to_output_layer = nn.Linear(8,1)# nn.Parameter(torch.rand(8,1))
# 必须使用forward作为方法名,因为Pytorch保留了,用其他的会报错!!!
def forward(self, x):
x = self.input_to_hidden_layer(x)
#若 nn.Parameter(torch.rand(2,8)),则x = x @ self.input_to_hidden_layer(x)
x = self.hidden_layer_activation(x)
x = self.hidden_to_output_layer(x)
#若 nn.Parameter(torch.rand(8,1)),则x = x @ self.hidden_to_output_layer(x)
return x
# 创建实例并注册到GPU上
mynet = MyNeuralNet().to(device)
# 获取参数权重看一下
mynet.input_to_hidden_layer.weight
# 更详细的看下参数
mynet.parameters()
for i in mynet.parameters():
print(i)
# 定义MSE损失
loss_func = nn.MSELoss()
# CrossEntropyLoss() # 多分类损失
# BCELoss # 二分类损失
# 计算损失
_Y = mynet(X)
loss_value = loss_func(_Y,Y)
# 在pytorch中约定先传预测,再传真实数据
print(loss_value)
# 优化器随机梯度下降
from torch.optim import SGD
opt = SGD(mynet.parameters(), lr = 0.001)
loss_history = []
for _ in range(50):
opt.zero_grad()# 刷新上一步计算的梯度
loss_value = loss_func(mynet(X),Y)
loss_value.backward()# 计算梯度
opt.step()# 根据梯度更新权重
loss_history.append(loss_value.item())
# 绘图
import matplotlib.pyplot as plt
%matplotlib inline
plt.plot(loss_history)
plt.title('Loss variation over increasing epochs')
plt.xlabel('epochs')
plt.ylabel('loss value')
2.4 数据集、数据加载器和批大小
批大小:用于计算损失或更新权重的数据点的数量
在有数百万数据点的情况下很有用,
class MyDataset(Dataset):
def __init__(self,x,y):
self.x = torch.tensor(x).float()
self.y = torch.tensor(y).float()
def __len__(self):
return len(self.x)
def __getitem__(self, ix):
return self.x[ix], self.y[ix]
ds = MyDataset(X, Y)
# 从ds中获取两个数据点
dl = DataLoader(ds, batch_size=2, shuffle=True)
# 其余没变可套用上方代码
# 变化见下
import time
loss_history = []
start = time.time()
for _ in range(50):
for data in dl:
x, y = data
opt.zero_grad()
loss_value = loss_func(mynet(x),y)
loss_value.backward()
opt.step()
loss_history.append(loss_value)
end = time.time()
print(end - start)
预测数据点
val_x = [[10,11]]
val_xf = torch.tensor(val_x).float().to('device')
mynet(val_x)
自定义损失函数
def my_mean_squared_error(_y, y):
loss = (_y-y)**2
loss = loss.mean()
return loss
my_mean_squared_error(mynet(X),Y)
# 和这个效果一样
loss_func = nn.MSELoss()
loss_value = loss_func(mynet(X),Y)
print(loss_value)
获取中间层的值(获取参数上面讲了)
# 种子
torch.random.manual_seed(10)
# 法1
input_to_hidden = mynet.input_to_hidden_layer(X)
hidden_activation = mynet.hidden_layer_activation(input_to_hidden)
x = mynet.hidden_to_output_layer(hidden_activation)
x
# 法2 改下类的forward函数
class MyNeuralNet(nn.Module):
def __init__(self):
super().__init__()
self.input_to_hidden_layer = nn.Linear(2,8)
self.hidden_layer_activation = nn.ReLU()
self.hidden_to_output_layer = nn.Linear(8,1)
def forward(self, x):
hidden1 = self.input_to_hidden_layer(x)
hidden2 = self.hidden_layer_activation(hidden1)
x = self.hidden_to_output_layer(hidden2)
return x, hidden1
# 调用
_Y, _Y_hidden = mynet(X)
2.5 使用Sequential类构建神经网络
之前是通过定义一个类来构建神经网络,
model = nn.Sequential(
nn.Linear(2, 8),
nn.ReLU(),
nn.Linear(8, 1)
).to(device)
# 这个包可以输出一个模型的摘要(总体架构)
!pip install torch_summary
from torchsummary import summary
# 输入模型和模型大小
summary(model, torch.zeros(2,2))
# 其余没变化除了模型名称
loss_func = nn.MSELoss()
from torch.optim import SGD
opt = SGD(model.parameters(), lr = 0.001)
import time
loss_history = []
start = time.time()
for _ in range(50):
for ix, iy in dl:
opt.zero_grad()
loss_value = loss_func(model(ix),iy)
loss_value.backward()
opt.step()
loss_history.append(loss_value)
end = time.time()
print(end - start)
# 预测新数据
val = [[8,9],[10,11],[1.5,2.5]]
val = torch.tensor(val).float().to(device)
model(val)
2.6 保存并加载Pytorch模型
保存模型
save_path = 'mymodel.pth'
torch.save(model.state_dict(), save_path)
# torch.save(model.state_dict(), 'mymodel.pth')
# state指的是模型的当前快照
# model.state_dict()返回一个字典
# 注:一个良好的保存做法是在调用torch.save()
之前将模型注册到CPU中,有助于模型加载到任何机器上(助人为乐)
# 所以可以torch.save(model.to('cpu').state_dict(), save_path)
加载模型
# 加载模型前需要事先对模型进行权重初始化
model = nn.Sequential(
nn.Linear(2, 8),
nn.ReLU(),
nn.Linear(8, 1)
).to(device)
# 然后再加载参数
state_dict = torch.load('mymodel.pth')
model.load_state_dict(state_dict)
# 预测数据
val = [[8,9],[10,11],[1.5,2.5]]
val = torch.tensor(val).float().to(device)
model(val)
标签:实战,loss,tensor,10,torch,Pytorch,np,视觉,hidden
From: https://www.cnblogs.com/dxlindex/p/18137047