深度学习(1)
01.张量的创建
tensor转numpy
使用tensor.numpy函数可以将张量转换为ndarray数组,但是共享内存,可以使用copy函数避免共享
import numpy as np
import torch
#print(torch.tensor(10))
#data_np=np.random.rand(2,3)
#print(torch.tensor(data_np))
#data=torch.tensor([[1,2.],[3,4]])
#print(data)
#print(torch.Tensor(2,3))
#print(torch.ShortTensor(2,3))
#print(torch.LongTensor(2,3))#int64,运用较广泛
#print(torch.FloatTensor(2,3))
#print(torch.DoubleTensor(2,3))
#创建线性张量
#print(torch.arange(1,10,2))#不包含终止值,2为步长
#print(torch.linspace(1,10,10))#10个数,包含终止值
#随机种子设置
#print(torch.rand(2,3))
#seed=torch.random.initial_seed()
#torch.random.manual_seed(seed)
#print(torch.rand(2,3))
#全01张量
data=torch.rand(4,5)
#print(torch.zeros(2,3))
#print(torch.zeros_like(data))
#print(torch.ones(2,3))
#print(torch.ones_like(data))
#print(torch.full((2,3),5))
#print(torch.full_like(data,5))
#元素类型转换
#print(data.long().dtype)
#print(data.float().dtype)
#print(data.double().dtype)
#print(data.short().dtype)
#print(data.int().dtype)
#print(data.type('torch.IntTensor').dtype)
import torch
import numpy as np
# torch.random.manual_seed(2)
#data_tensor=torch.randint(1,10,(2,3))
# print(type(data_tensor))
#
data_numpy=data_tensor.numpy().copy()
# print(type(data_numpy))
#
# data_numpy[0][0]=100
# print(data_tensor)
# print(data_numpy)
# data_numpy1=np.array([1,2,3])
data_tensor=torch.from_numpy(data_numpy.copy())
data_tensor=torch.Tensor(data_numpy)
# data_tensor[0]=100
# print(data_numpy)
# print(data_tensor)
#当张量只包含一个元素时,可以通过item()方法提取出该值
data=torch.tensor([1,])
print(data.item())
02张量的基本运算
import torch
# torch.random.manual_seed(22)
# data1=torch.randint(0,10,[2,3])
# print(data1)
#
# # print(data.add(10))
# # print(data.sub(10))
# # print(data.mul(10))
# # print(data.div(10))
# # print(data.neg()
# # #add_等带下划线的版本会修改原数据
#
# torch.random.manual_seed(22)
# data2=torch.randint(0,10,[2,3])
# print(data2)
#
# print(torch.mul(data1,data2))#点乘
#
# print(data1*data2)
#矩阵乘法
torch.random.manual_seed(22)
data1=torch.randint(0,10,[3,4])
print(data1)
torch.random.manual_seed(22)
data2=torch.randint(0,10,[4,5])
print(data2)
print(data1 @ data2)
print(torch.matmul(data1,data2))
03张量的运算函数
import torch
torch.random.manual_seed(22)
data=torch.randn([3,4])
print(data)
print(data.mean)
print(data.mean(dim=0))
print(data.mean(dim=1))
print(data.sum())
print(data.sum(dim=0))
print(data.sum(dim=1))
print(data.sqrt())
print(torch.pow(data,2))
print(torch.pow(2,data))
print(torch.exp())
print(torch.log(data))
print(torch.log10(data))
print(torch.log2(data))
04张量的形状操作
1.reshape函数可以在保证张量数据不变的前提下改变数据的维度
2.squeeze和unsqueeze函数可以用来增加或减少维度
3.transpose函数可以实现交换张量形状的指定维度,permute可以一次交换更多的维度
4.view函数也可以用于修改张量的形状,但他要求被转换的张量内存必须连续,所以一般配合contiguous函数使用
5.cat函数可以将张量按照指定的维度拼接起来,其他维度要求相同
import torch
torch.manual_seed(22)
data1=torch.randint(0,10,[2,3])
# print(data1.shape)
# print(data1.size(2))
# print(data1.reshape(4,-1,3).shape)
#unsqueeeze与squeeze增加或减少维度
# data2=data1.unsqueeze(dim=1).unsqueeze(dim=-1)
# print(data2.shape)
# print(data2.squeeze().shape)
#transpose与permute转置
# data2=torch.transpose(data1,0,2)
# data3=torch.transpose(data2,1,2)
# data4=torch.transpose(data3,2,3)
# print(data4.shape)
# print(data1.permute(0,2,3,1).shape)
# print(torch.permute(data1,[0,2,3,1]).shape)
print(data1.is_contiguous())
print(data1.view(3,-1).shape)
data2=torch.transpose(data1,0,1)
print(data2.is_contiguous())
data3=data2.contiguous()
print(data3.view(3,-1).shape)
if data1.is_contiguous():
data1.view(-1)
else:
data1.contiguous().view(-1)
05自动微分
import torch
#数据 特征+目标
# x=torch.tensor(5)
# y=torch.tensor(0.)
x=torch.ones(2,5)
y=torch.zeros(2,3)
#参数 权值+偏置
# w=torch.tensor(1,requires_grad=True,dtype=torch.float32)
# b=torch.tensor(3,requires_grad=True,dtype=torch.float32)
w=torch.randn(5,3,requires_grad=True)
b=torch.randn(3,requires_grad=True)
#预测
# z=w*x+b
z=torch.matmul(x,w)+b
#损失
loss=torch.nn.MSELoss()
loss=loss(z,y)
#微分
loss.backward()
#梯度
print(w.grad)
print(b.grad)
06线性回归案例
import numpy
import torch
from sklearn.datasets import make_regression
import matplotlib.pyplot as plt
from torch.utils.data import TensorDataset, DataLoader
x,y,coef=make_regression(n_samples=100,n_features=1,noise=10,coef=True)
'''
n_samples=100:生成 100 组样本
n_features=1:每个样本只有1 个自变量 X(一元线性)
noise=10:加入高斯噪声,数值越大散点越乱
coef=True:返回真实权重系数 coef(默认不返回)'''
# plt.scatter(x,y)#画散点图
y_true=[coef*v+1.5 for v in x]
plt.plot(x,y_true)#画连续直线
plt.show()
#数据获取
x=torch.tensor(x)
y=torch.tensor(y)
dataset=TensorDataset(x,y)#张量数据集容器,作用:把特征 x 和标签 y 一一配对打包,dataset[i] = (x[i], y[i]),取第 i 组数据。
dataloader=DataLoader( dataset=dataset,batch_size=8,shuffle=True,drop_last=False)#分批迭代器,用来分批次取数据,每次遍历全部数据前打乱样本顺序(训练防止过拟合)
#模型构建
model=torch.nn.Linear(1,1)#输入特征数为1,输出特征数为1的线性模型
#模型训练
#损失函数
loss=torch.nn.MSELoss()#均方误差损失函数,适用于回归问题
#优化器
optimizer=torch.optim.SGD(model.parameters(),lr=0.001)
#遍历
loss_num=[]
for i in range(100):
sum=0
sample=0
#获取batch数据
for x,y in dataloader:
#模型预测
x=x.float()
y_pred=model(x)#y_pred:Linear(1,1)输出 → [batch, 1] → (2,1)
#损失计算
l=loss(y_pred,y.reshape(-1,1))#y的形状为torch.Size([2]),需要调整为torch.Size([2,1]),与y_pred的形状一致
sum+=l.item()
sample=len(y)#取出当前这一批数据的样本个数2
#梯度清零
optimizer.zero_grad()
#自动微分
l.backward()
#参数更新
optimizer.step()
loss_num.append(sum/sample)
#画损失曲线
plt.plot(range(100),loss_num)
plt.grid()
plt.show()
#绘制拟合直线
x=torch.linspace(x.min(),x.max(),1000)# 从原始X最小值~最大值,均匀生成1000个连续x点
#linspace(a,b,N):在[min,max]之间均分 1000 个浮点数据,用来画光滑直线。
w=model.weight.detach().item()#取出权重值,detach切断梯度,item()取出数值
b=model.bias.detach().item()#取出偏置值,detach切断梯度,item()取出数值
y1=torch.tensor([v*w+b for v in x])
y2=torch.tensor([v*coef+1.5 for v in x])
plt.plot(x,y1,label='train')
plt.plot(x,y2,label='true')
plt.grid()#显示背景网格
plt.legend()#在图片空白处生成图例,区分训练拟合线、真实基准线;
plt.show()

07常见的激活函数-sigmoid 激活函数
- sigmoid 函数可以将任意的输入映射到
之间,当输入的值大致在
或者
时,意味着输入任何值得到的激活值都是差不多的,这样会丢失部分的信息。比如:输入 100 和输出 10000 经过 sigmoid 的激活值几乎都是等于 1 的,但是输入的数据之间相差 100 倍的信息就丢失了。 - 对于 sigmoid 函数而言,输入值在
之间输出值才会有明显差异,输入值在
之间才会有比较好的效果。 - 通过上述导数图像,我们发现导数数值范围是
,当输入
或者
时,sigmoid 激活函数图像的导数接近为 0,此时网络参数将更新极其缓慢,或者无法更新。 - 一般来说,sigmoid 网络在 5 层之内就会产生梯度消失现象。而且,该激活函数并不是以 0 为中心的,所以在实践中这种激活函数使用的很少。sigmoid函数一般只用于二分类的输出层。

import torch
import matplotlib.pyplot as plt
x = torch.linspace(-20, 20, 1000)
y = torch.sigmoid(x)
plt.plot(x, y)
plt.grid()
plt.show()
#导函数
x=torch.linspace(-20,20,1000,requires_grad=True)
torch.sigmoid( x).sum().backward()#sum():把所有张量元素求和,把多维张量压缩成单个标量(一个数字)
#PyTorch 规定:只有标量才能直接调用.backward()做自动求导
plt.plot(x.detach(), x.grad)
plt.grid()
plt.show()
'''
完整训练一轮顺序
前向传播:
x→预测→算 loss
反向传播:
loss.backward()求梯度
优化器:
opt.step() w=w−lr×grad 更新参数
'''
08常见的激活函数 -tanh 激活函数
- Tanh 函数将输入映射到 (−1,1) 之间,图像以 0 为中心,在 0 点对称,当输入大概<−3或者>3时将被映射为 -1 或者 1。其导数值范围 (0,1),当输入的值大概<−3或者>3时,其导数近似 0。
- 与 Sigmoid 相比,它是以 0 为中心的,且梯度相对于 sigmoid 大,使得其收敛速度要比 Sigmoid 快,减少迭代次数。然而,从图中可以看出,Tanh 两侧的导数也为 0,同样会造成梯度消失。
- 若使用时可在隐藏层使用 tanh 函数,在输出层使用 sigmoid 函数。

09常用的激活函数 - ReLU 激活函数
- ReLU 激活函数将小于 0 的值映射为 0,而大于 0 的值则保持不变,它更加重视正信号,而忽略负信号,这种激活函数运算更为简单,能够提高模型的训练效率。
- 当x<0时,ReLU 导数为 0,而当x>0时,则不存在饱和问题。所以,ReLU 能够在x>0时保持梯度不衰减,从而缓解梯度消失问题。然而,随着训练的推进,部分输入会落入小于 0 区域,导致对应权重无法更新。这种现象被称为 “神经元死亡”。
- ReLU 是目前最常用的激活函数。与 sigmoid 相比,RELU 的优势是: 采用 sigmoid 函数,计算量大(指数运算),反向传播求误差梯度时,计算量相对大,而采用 Relu 激活函数,整个过程的计算量节省很多。sigmoid 函数反向传播时,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练。Relu 会使一部分神经元的输出为 0,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生。

10常用的激活函数-SoftMax激活函数
softmax就是将网络输出的logits通过softmax函数,就映射成为(0,1)的值,而这些值的累加和为1(满足概率的性质),理解成概率,选取概率最大(也就是值对应最大的)节点,作为我们的预测目标类别。

激活函数的选择方法
激活函数的作用:向神经网络中添加非线性因素
对于隐藏层:
- 优先选择 ReLU 激活函数
- 如果 ReLu 效果不好,那么尝试其他激活,如 Leaky ReLu 等。
- 如果你使用了 ReLU,需要注意一下 Dead ReLU 问题,避免出现大的梯度从而导致过多的神经元死亡。
- 少使用 sigmoid 激活函数,可以尝试使用 tanh 激活函数
对于输出层:
- 二分类问题选择 sigmoid 激活函数
- 多分类问题选择 softmax 激活函数
- 回归问题选择 identity 激活函数

11参数初始化
初始化方法的选择
一般我们在使用PyTorch构建网络模型时,每个网络层的参数都有默认的初始化方法,优先选择kaming的初始
化,xavier初始化方式。
均匀分布初始化
权重参数初始化从区间均匀随机取值。即在(-1/d1/2,1/d1/2)均匀分布中生成当前神经元的权重,其中d为每个神经元的输入数量
正态分布初始化
随机初始化从均值为0,标准差是1的高斯分布中取样,使用一些很小的值对参数W进行初始化
全0初始化
将神经网络中的所有权重参数初始化为0
全1初始化
将神经网络中的所有权重参数初始化为1.
固定值初始化
将神经网络中的所有权重参数初始化为某个固定值.
kaiming初始化,也叫做HE初始化
HE初始化分为正态分布的HE初始化、均匀分布的HE初始化.
正态化的he初始化
stddev = sqrt(2/fan_in)
均匀分布的he初始化
它从[-limit,limit]中的均匀分布中抽取样本,limit是sqrt(6/fan_in)
fan_in输入神经元的个数
xavier初始化,也叫做Glorot初始化
该方法也有两种,一种是正态分布的xavier初始化、一种是均匀分布的xavier初始化
正态化的Xavier初始化
stddev = sqrt(2/ (fan_in + fan_out))
均匀分布的Xavier初始化
[-limit, limit]中的均匀分布中抽样本, limit 是sqrt(6/(fan_in +fan_out)
fan_in是输入神经元的个数,fan_out是输出的神经元个数
12模型构建
1.神经网络的搭建方法
定义继承自nn.Module的模型类
在_init_方法中定义网络中的层结构
在forward方法中定义数据传输方式
2.网络参数量的统计方法
统计每一层中的权重w和偏置b的数量

import torch
from torch import nn
from torchsummary import summary
class model(nn.Module):
def __init__(self):
super().__init__()
self.linear1=nn.Linear(3,3)
nn.init.kaiming_normal_(self.linear1.weight)
self.linear2=nn.Linear(3,2)
nn.init.xavier_normal_(self.linear2.weight)
self.out=nn.Linear(2,2)
nn.init.uniform_(self.out.weight)
def forward(self,x):
x_layer1=self.linear1(x)
x_layer1=torch.sigmoid(x_layer1)
x_layer2=self.linear2(x_layer1)
x_layer2=torch.relu(x_layer2)
out=self.out(x_layer2)
out=torch.softmax(out,dim=-1)#永远在最后一维做softmax,适配各种形状的分类输出,该维度上所有数字 → 转为概率
return out
if __name__=="__main__":
my_model=model()
x=torch.randn(10,3)
out=my_model(x)
print(out.shape)
summary(my_model,(3,),batch_size=10)
for name,parameters in my_model.named_parameters():
print(name)
print(parameters)
神经网络的优缺点
1.优点
精度高,性能优于其他的机器学习方法,甚至在某些领域超过了人类
可以近似任意的非线性函数随之计算机硬件的发展,
近年来在学界和业界受到了热捧,有大量的框架和库可供调。
2.缺点
黑箱,很难解释模型是怎么工作的
训练时间长,需要大量的计算力
网络结构复杂,需要调整超参数
小数据集上表现不佳,容易发生过拟合
13分类任务的损失函数
多分类损失函数——交叉熵
损失函数是用来衡量模型参数的质量的函数
在多分类任务通常使用softmax将Iogits转换为概率的形式,所以多分类的交叉熵损失也叫做softmax损失,它的计算方法是:

其中:
1.y是样本x属于某一个类别的真实概率
2.而f(x)是样本属于某一类别的预测分数
3.S是softmax激活函数,将属于某一类别的预测分数转换成概率
4.L用来衡量真实值y和预测值f(x)之间差异性的损失结果
import torch
import torch.nn as nn
#y_true=torch.tensor([0,1,2],dtype=torch.int64)
y_true=torch.tensor([[1,0,0],[0,1,0],[0,0,1]],dtype=torch.float32)
y_pred=torch.tensor([[8,9,10],[12,4,6],[3,8,6]],dtype=torch.float32)
loss_fn=nn.CrossEntropyLoss()
print(loss_fn(y_pred,y_true))
二分类任务损失函数——交叉熵
在处理二分类任务时,我们不再使用softmax激活函数,而是使用sigmoid激活函数,那损失函数也相应的进行调整,使用二分类的交叉熵损失函数:

其中:
1.y是样本x属于某一个类别的真实概率
2.而y^是样本属于某一类别的预测概率
3.L用来衡量真实值y与预测值y^之间差异性的损失结果。
在pytorch中实现时使用nn.BCELoss(),如下所示:
y_true=torch.tensor([0,1,0,1],dtype=torch.float32)
y_pred=torch.tensor([0.1,0.9,0.2,0.8],dtype=torch.float32)
loss_fn=nn.BCELoss()
print(loss_fn(y_pred,y_true))
14.回归任务的损失函数
MAE损失函数

MSE损失函数

smooth L1损失函数

15梯度下降算法
梯度下降法是一种寻找使损失函数最小化的方法。从数学上的角度来看,梯度的方向是函数增长速度最快的方向,那么梯度的反方向就是函数减少最快的方向,所以有:

其中,n是学习率,如果学习率太小,那么每次训练之后得到的效果都太小,增大训练的时间成本。如果,学习率太大,那就有可能直接跳过最优解,进入无限的训练中。解决的方法就是,学习率也需要随着训练的进行而变化。
在进行模型训练时,有三个基础的概念:
1.Epoch:使用全部数据对模型进行以此完整训练,训练轮次
2.Batch_size:使用训练集中的小部分样本对模型权重进行以此反向传播的参数更新,每次训练每批次样本数量3.Iteration:使用一个Batch数据对模型进行一次参数更新的过程
假设数据集有50000个训练样本,现在选择Batch Size=256对模型进行训练。每个Epoch要训练的图片数量:50000
训练集具有的Batch个数:50000/256+1=196
每个Epoch具有的Iteration个数:196
10个Epoch具有的Iteration 个数:1960
前向传播和反向传播是什么?
前向传播:指的是数据输入的神经网络中,逐层向前传输,一直到运算到输出层为止。
反向传播(Back Propagation):利用损失函数ERROR,从后往前,结合梯度下降算法,依次求各个参数的偏导,并进行参数更新
更多推荐

所有评论(0)