深度学习概论
机器学习-深度学习
思维导图

为什么学习深度学习?
1.旧的技术不能解决当前的问题
2.旧的技术解决当前问题过于复杂(安全)
深度学习的模块
文本 - RNN - 循环神经网络 - 词嵌入层+循环层+全连接层
图像 - CNN - 卷积神经网络 - 卷积层+池化层+全连接层
全连接层 - ANN - 人工神经网络
人工神经网络 - 张量
核心点提炼
X * + Z(预测值) Loss(损失) 求导->梯度 --------------->正向传播
W B Y(真实值) <---------------反向传播(梯度更新公式:W新 = W旧 - 学习率*梯度)
神经元( 信息的求和 | 激活函数 )
全连接层 = 当前层的每一个神经元都由上一层的所有神经元进行连接
深度学习的特点
1.由神经网络+神经搭建而成
输入层(特征) ---- 隐藏层 ---- 输出层
- 特征工程不需要人工处理,而是由算法自动算法
- 大数据 + 算力(可以使用GPU加速)
- 可解释性差,隐藏层是一个黑盒子,至于为什么是3层,5层,18层,当前业界公认无法解释
张量的使用
张量的创建
# 基于小写的tensor
指定数据 - torch.tensor
# 基于大写的Tensor
指定数据 + 指定维度 - torch.Tensor
#基于指定的类型进行创建 int16 32 64 float16 32 64 <-> short int long half float double
torch.IntTensor(2,3) IntTensor([1,2,3])
线性张量
torch.arange(起始,结束,步长) -> 包左不包右
torch.linspace(起始,结束,划分数量) -> 包左包右
随机张量
随机种子:
看 - torch.initital_seed()
改 - torch.manual_seed()
随机数
0-1 - torch.rand()
-3-3 - torch.randn()
随机整数
torch.randint(起始,结束,尺寸) ->包左不包右
全0全1
ones(尺寸) 通过尺寸创建
ones_like(张量) 通过张量的形状创建
zeros(尺寸) 通过尺寸创建
zeros_like(张量) 通过张量的形状创建
创建指定张量
torch.full(size,填充值)
torch.full_like(张量,填充值)
数据类型转换
data = torch.tensor([1,2,3])
data.type(指定的类型) #int16 int32 int64 float16 float32 float64.....
data.type(torch.IntTensor) #了解即可
data.half() data.int() #了解即可
tensor和numpy的转换
tensor -> numpy
tensor = torch.tensor([1,2,3])
tensor = tensor.numpy()#转换,共享
tensor = tensor.numpy().copy()#转换,不共享
numpy -> tensor
tensor = torch.form_numpy(numpy)#转换,共享
tensor = torch.form_numpy(numpy.copy)#转换,共享
tensor = torch.tensor(numpy)#创建,不共享
从一个数据的tensor中取出值
data1 = tensor = torch.tensor(1)
data2 = tensor = torch.tensor([1])
data1.item()
简单计算
加 减 乘 除 负号
add sub mul div neg
add_ sub_ mul_ div_ neg_ 带下划线表示修改原数据
掌握 :+ - * /
# 掌握:
# 1.+ - * /
# 2.pytorch中,所有带_都表示更新原数据
import torch
# 加减乘除取负号
# add sub mul div neg
# + - * /
# 1.准备数据
data = torch.tensor([[-1, 2], [3, 4], [5, 6]])
# 2.1-加法:加常数
t1 = torch.tensor([[1, 1], [2, 2], [3, 3]])
print(data + 1)
# 2.2-加法:加tensor
print(data + t1)
print(t1.add(data)) # 同上
# 2.3把data加到t1上,然后更新t1
t1.add_(data)
print(t1)
# 其他运算演示
print(data-t1)
print(data.sub(t1))#同上
print(data*t1)
print(data.mul(t1))#同上
print(data/t1)
print(data.div(t1))#同上
# 3.取负号
print(data.neg())
print(data*-1)#同上
点积点乘
点乘 - mul *
两个形状相同的张量相乘=每个位置相乘
点积(没有交换律) - matmul @
1.前提:两个张量能点积的 = a的列等于b的行
2.过程:a的行 * b的列
3.结果:a行b列
掌握 :@ matmul
# 掌握: @
import torch
t1 = torch.tensor([[1,2],[2,3]])
t2 = torch.tensor([[2,2],[3,3]])
# 点乘
print(t1*t2)
print(t1.mul(t2))
t3 = torch.tensor([[1,2],[2,3],[3,4]])
# 点积
# print(t2@t3)
print(t3@t2)
# print(torch.matmul(t2,t3))
print(torch.matmul(t3,t2))
# 补充:
# print(t3.sign()) #带符号的全1
# print(t3.square()) #平方
# print(t3.fmod(2)) #取余
# ......
运算函数
1.求和 sum :dim表示第几维进行求和
2.平均值mean/最大值max/最小值min
3.开方sqrt/平方square/幂pow/绝对值abs\absolute
4.指数exp
5.对数log/log2/log10
掌握 sum
# 掌握:sum
import torch
# 0.准备数据
data = torch.tensor(
# 第0维度:将下面括号内的数据进行求和,合并成1条数据
[
# 第1维度:将下面括号内的数据进行求和,合并成1条数据
[1,2,3],
[4,5,6]
]
)
# 1.求和sum - 维度
print(data.sum(dim=0)) #第0维,列求和
print(data.sum(dim=1)) #第1维,行求和
# print(data.sum(dim=2)) #?报错!!!超出dim区间!!
print(data.sum()) #整个求和
# 2.平均、最大值、最小值
# (1+2+3+4+5+6)/6 计算会有小数,所以需要的是float类型,但是现在是int类型
data = data.type(torch.float32)
print(data.mean()) #平均值一般都不是整数,所以只有float类型才能进行平均值的计算
print(data.max())
print(data.min())
# 3.开方 平方 幂 绝对值
data.sqrt_() #开方
print(data)
data.square_() #平方
print(data)
data.pow_(2) #2次幂 === 平方
print(data)
data = data*-1
print('负1的data',data)
data.abs_() # 绝对值
data.absolute_() # 绝对值
print('绝对值data',data)
# 4.指数e
print(data.exp())
# 5.对数
print(data.log())
print(data.log2())
print(data.log10())
索引使用
掌握 所有(除了多维索引)
0.准备数据
data = torch.randint(1,10,(3,4))
# 第0维 # [ # 第1维 # [5, 6, 2, 4, 2], # [4, 7, 3, 8, 2], # [8, 1, 3, 3, 1], # [5, 4, 7, 9, 9] # ]1.简单行列索引
data[0](语法糖) - 人话:取第0行 - 逻辑:取0维的第一个
data[0,:] (效果同上)
data[:,0] - 人话:取第1列 - 逻辑:取0维所有,取1维第一
2.列表索引
人话:[[行索引],[列索引]]]
逻辑:[[0维索引],[1维索引]]
data[[0,1],[1,2]] #取的是(0,1),(1,2)
data[[1,3],[2,4]] #取的是(1,2),(3,4)
3.范围索引(切片)
data[:3,:4] #取前2行,前3列
4.布尔索引
data>5 #得到布尔值索引
data[data>5] #获取原数据布尔值所引为True的数据
5.多维索引
mul_data = torch.randint(1,10,(3,4,5))
# 第0维数据([]里面的内容是第0维数据)-0轴 - 3个数据 # [ # 第1维数据([]里面的内容是第1维数据)-1轴 - 4个数据 # [ # 第2维数据([]里面的内容是第2维数据)-2轴 -5数据 # [1, 6, 7, 2, 1], # [6, 6, 4, 5, 9], # [7, 2, 5, 5, 7], # [6, 9, 8, 9, 1] # ], # [ # [5, 1, 6, 1, 8], # [7, 8, 9, 1, 1], # [3, 5, 2, 4, 3], # [5, 7, 8, 2, 6] # ], # [ # [5, 6, 1, 7, 1], # [8, 6, 9, 8, 8], # [1, 3, 6, 2, 9], # [9, 9, 5, 2, 6] # ] # ] # 需求1:取出0轴第1个数据 # [ # [1, 6, 7, 2, 1], # [6, 6, 4, 5, 9], # [7, 2, 5, 5, 7], # [6, 9, 8, 9, 1] # ] # 需求2:取出1轴第1个数据 # 理解:取出0轴所有的数据,1轴第1个数据 # 过程:0轴第1个1轴第1个数据 0轴第2个1轴第1个数据 0轴第3个1轴第1个数据 # [ # [1, 6, 7, 2, 1] # ] # [ # [5, 1, 6, 1, 8] # ], # [ # [5, 6, 1, 7, 1] # ] # 需求3:取出2轴第1个数据 # 翻译:取出0轴所有的数据,取出1轴所有的数据,取出2轴的第1个数据 # 说明: # ( # 0轴第"1"个1轴第"1"个, # 0轴第"1"个1轴第"2"个 # 0轴第"1"个1轴第"3"个 # 0轴第"1"个1轴第"4"个 # ) # 结果: [1,6,7,6] # ( # 0轴第"2"个1轴第"1"个, # 0轴第"2"个1轴第"2"个 # 0轴第"2"个1轴第"3"个 # 0轴第"2"个1轴第"4"个 # ) # 结果: [5,7,3,5] # ( # 0轴第"3"个1轴第"1"个, # 0轴第"3"个1轴第"2"个 # 0轴第"3"个1轴第"3"个 # 0轴第"3"个1轴第"4"个 # ) # 结果:[5,8,1,9] # 最终结果:[[1,6,7,6],[5,7,3,5],[5,8,1,9]] # 总结:多维索引每一个维度都要考虑到,如果没有明确说明要或者不要,那表示全都要!!!
张量形状改变
掌握:reshape squeeze unsqueeze transpose permute
reshape 不改变原数据的前提下,改变形状
squeeze降维 - 在原数据基础上将所有维度为1删除
unsqueeze升维 - 在原数据基础上提升一个维度
transpose交换任意两个维度
permute交换任意个数的维度
[了解] view改变形状(数据保持连续性) + contiguous(修改数据内存,保持数据连续)
import torch
# 0.准备数据
data = torch.tensor([[1,2,3],[4,5,6]])
# 1.使用reshape进行数据的修改(2,3 -> 1,6)
t1 = data.reshape(1,6) # [[1,2,3],[4,5,6]]->[[1, 2, 3, 4, 5, 6]]
print(t1,t1.data_ptr(),t1.stride()) #data - pointer 数据指针,stride表示在内存中读取数据的步长
# 2.使用reshape进行数据的修改(2,3 -> 1,?)
t2 = data.reshape(1,-1) #会自动计算-1变成6
print(t2,t2.data_ptr(),t2.stride()) #data - pointer 数据指针,stride表示在内存中读取数据的步长
# 3.使用reshape进行数据的修改(2,3 -> 2,?)
t3 = data.reshape(3,-1) #会自动计算-1变成2
print(t3,t3.data_ptr(),t3.stride()) #data - pointer 数据指针,stride表示在内存中读取数据的步长
## 4.使用reshape进行数据的修改(2,3 -> 4,2)
# t4 = data.reshape(4,2) #报错
# print(t4)
# reshape可以在满足前提的情况下修改数据
# 【拓展】张量数据创建的时候,在底层就已经分类内存,将对应的值写入内存中
# 1.张量数据不变 ===> 内存中的内容没有发生变化
# |----|
# | 1 |
# |----|
# |----|
# | 2 |
# |----|
# |----|
# | 3 |
# |----|
# |----|
# | 4 |
# |----|
# |----|
# | 5 |
# |----|
# |----|
# | 6 |
# |----|
# 2.数据不变,为什么形状可以变化====>步长计算公式——》不同形状下的数据应该是什么样!!
张量的拼接
掌握 全部
cat - 不会提升维度 - 除了拼接的维度不一样,其他维度必须一样
stack - 会提升维度 - 所有数据的维度必须完全一致
总结:
cat - 其他维度不变,拼接维度做加法
stack - 其他维度不变,升哪个维度,哪个维度就等于拼接的数据条数
# 给豆包发消息:图片(张量)+文字(张量) = 1条消息(张量)
import torch
# -------------------------------------cat拼接(不会提升维度数)
# 要求:两个数据进行指定维度的拼接,除了指定的维度数可以不一样,其他数量必须一样
# 0.准备数据
data1 = torch.tensor([[1,2,3],[4,5,6]]) #2行3列 dim0=2 dim1=3
data2 = torch.tensor([[1,2,3],[4,5,6],[4,5,6]]) #3行3列 dim0=2 dim1=3
# 1.按照dim=0进行拼接data1和data2
data = torch.cat([data1,data2],dim=0)
print(data)
# 2.按照dim=1进行拼接data1和data2
data3 = torch.tensor([[1,2,3,4,5],[1,2,3,4,5]]) #2行5列
data = torch.cat([data1,data3],dim=1)
print(data)
# -------------------------------------stack拼接(会提升维度数)
# 0.准备数据(维度必须完全一致)
data4 = torch.tensor([[1,2,3],[4,5,6]]) #2,3
data5 = torch.tensor([[1,2,3],[4,5,6]]) #2,3
data6 = torch.tensor([[1,2,3],[4,5,6]]) #2,3
data = torch.stack([data4,data5,data6],dim=0) #0维进行提升
print(data.shape)#[3],2,3
# 2,3 + 2,3 + 2,3 +dim =0 _> 3,2,3
data = torch.stack([data4,data5,data6],dim=1) #1维进行提升
print(data.shape)#2,[3],3
# 2,3 + 2,3 + 2,3 +dim =1 _> 2,3,3
data = torch.stack([data4,data5,data6],dim=2) #2维进行提升
print(data.shape)#2,3,[3]
# 2,3 + 2,3 + 2,3 +dim =2 _> 2,3,3
# 总结:其他维度不变,升哪个维度,哪个维度就等于数据的条数
# 需求:2行3列+2行3列
# 如果dim = 0 -> 2行3列+2行3列 = [2]个2行3列
# 如果dim = 1 -> 2行3列+2行3列 = 2个[2]行3列
# 如果dim = 2 -> 2行3列+2行3列 = 2个[3]行[2]列
# 都是2行3列进行拼接,指定哪个维度,哪个唯独就是新提升上来的
# 2,3 + 2,3 +dim=0 = > 【2】,2,3
# 2,3 + 2,3 +dim=1= > 2,【2】,3
# 2,3 + 2,3 +dim=2= > 2,3,【2】
自动微分
函数.backward()
函数必须是设置了require_grad属性的相关函数
作用:求导
x = torch.linspace(-20,20,1000,requires_grad=True) y = torch.sigmoid(x) #定义一个函数 y.sum().backward() #对函数求导
自动微分的使用
X = torch.ones(2,5)
W = torch.randn(5,3)
B = torch.randn(3)
Z = X@W +B
Y = torch.zeros(2,3)
loss_fn = MSELoss()
loss = loss_fn(Z,Y)
#自动微分
loss.sum().backward()
w.grad b.grad
模拟线性回归
1.准备数据 make_regressor( 样本数量,特征数量,标签数量,噪声,coef,bias )
2.数据->张量->数据集->数据加载器
3.训练模型
准备model
准备优化器optimizer
3.1模型预测
y_pre = model(x)
3.2定义损失函数
loss_fn = MSELoss()
3.3计算损失
loss = loss_fn(y_pre,y)
3.4自动微分
loss.backward()
3.5更新梯度
optimizer.step()
3.6清空梯度
optimizer.zero_grad()
3遍起步
import torch
from sklearn.datasets import make_regression #创建线性回归数据
from torch.utils.data import TensorDataset,DataLoader #数据集/数据加载器
import matplotlib.pyplot as plt
import matplotlib
# matplotlib中文支持
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['axes.unicode_minus'] = False
# 1.创建数据
def create_data():
# Z = KX +B k斜率 b偏置
# 已知X 已知Y - 用于训练模型,让模型可以进行预测
# 已知k 已知b - 用于检测最终我们的模型预测结果的
# bias = 3,x,y,coef?
x,y,coef = make_regression(
n_samples = 100, #样本数量
n_features = 1, #特征数量
n_targets = 1, #标签数量
bias = 3, #偏置的值(自定义)
noise = 10, #噪声(如果没噪声就成了完美直线了)
coef=True, #是否计算斜率
random_state=3
)
# print(x)
# print(y.reshape(-1,1))
print(coef) # 斜率:27.478050549563925
# bias = 3 # 偏置:3
return x,y.reshape(-1,1),coef
# 2.模型训练
def train(x,y,coef):
# 数据->张量->数据集->数据加载器(多轮训练的依赖)
# x特征,y标签 -> coef bias
# 1.数据(100)-> 张量
x = torch.tensor(x,dtype=torch.float32)
y = torch.tensor(y,dtype=torch.float32)
# 2.张量 -> 数据集
dataset = TensorDataset(x,y)
# 3.数据集 -> 数据加载器
# 参数说明:
# 1.数据集 - 100条数据
# 2.批次训练数据量 - 16条数据,每次从100条中取16条,训练7次才能结束
# 3.数据是否随机
dataloader = DataLoader(dataset,batch_size=16,shuffle=True)
# # 查看数据
# for x_train,y_train in dataloader:
# print(x_train.shape)
# print(y_train.shape)
# 4.模型训练
# 0.准备工作
# 模型创建 - 参数说明:1.特征数量 2.标签数量
model = torch.nn.Linear(in_features=1,out_features=1)
# 优化器创建 - 参数说明:1.模型的参数 2.学习率
optimizer = torch.optim.SGD(params=model.parameters(),lr=0.01)
# 5.计算损失
# 5.1 - 总损失 - 记录每一批次的损失(画图)
loss_list = []
# 100轮 = 每轮7次 = 700批次
for i in range(100):
# 5.2 - 每一轮的详细损失:总损失 ,平均损失
total_loss,train_count = 0,0
# 训练7次,每批16条数据 / 一轮 100->16/16/16/16/16/16/4
for x_train, y_train in dataloader:
# -----------------------------------前向传播
# 1.模型预测
y_pre = model(x_train)
# 2.损失函数
loss_fn = torch.nn.MSELoss()
loss = loss_fn(y_pre, y_train)
# loss_list.append(loss) #记录到损失的列表中(记每一次,100*7)
total_loss += loss.item() #记录每一轮的总损失
train_count += 1 #几轮训练的次数
# -----------------------------------反向传播
# 3.自动微分:获取到梯度
loss.backward()
# 4.更新梯度:优化器实现
optimizer.step()
# 5.梯度清零
optimizer.zero_grad()
print(f"第{i+1}轮训练,平均损失:{total_loss/train_count}")
loss_list.append(total_loss/train_count) #每一轮的平均损失
#训练结束:训练结果在模型身上
print(f"训练结束,查看模型结果:{model.state_dict()}")
print(f"训练结束,查看真实结果:{coef},3")
# 6.数据可视化
print(len(loss_list))
# 6.1绘制损失下降的曲线 - 关于损失的
# plt.scatter(range(len(loss_list)),loss_list)
# plt.title("损失下降曲线")
# plt.xlabel("训练次数")
# plt.ylabel("损失值")
# plt.show()
# 6.2绘制样本数据的点
plt.scatter(x,y)
# 6.3绘制真实的直线
# x , y = coef*x+3
#细节点1-报错原因:'numpy.ndarray' and 'Tensor' -> numpy数据不能和tensor数据进行运算,但是tensor数据可以和numpy数据进行运算
plt.plot(x,x*coef+3,color="red")
# 6.4绘制预测的直线
#细节点2-报错原因:因为i不能直接转numpy需要先detach()再numpy()
plt.plot(x,[i.detach().numpy() for i in model(x)],color="green")
plt.title("预测结果对比")
plt.xlabel("特征值")
plt.ylabel("标签值")
plt.legend(["样本数据","真实直线","预测直线"])
plt.show()
if __name__ == '__main__':
# 已知x特征 y标签 以及真实的斜率/偏置
x,y,coef = create_data()
# 模型训练(给你x,y,自己算W B)
train(x,y,coef)
# 模型训练总结:(神经元的加权求和)
# 1.数据准备:数据->张量->数据集->数据加载器
# 2.模型准备:模型创建 / 优化器创建
# 3.模型训练:训练几轮?每轮训几批?每批训几条?
# 4.训练流程:正向(前向)传播 / 反向传播
# 前向传播:模型预测/损失函数/损失
# 反向传播:自动微分/更新梯度/梯度清零
# 神经元(加权求和|激活函数)
# 绘图:
# 统计损失,看一下损失的变化 ,预测直线和真实直线以及样本之间的差异
