动手学深度学习基础

第一次作业:深度学习基础

1. 图像处理基本练习

展示不同通道上的图像

# 展示三通道图像
plt.subplot(121)
plt.imshow(colony[:,:,:])
plt.title('3-channel image')
plt.axis('off')

# 展示只有一个通道的图像
plt.subplot(122)
plt.imshow(colony[:,:,0])
plt.title('1-channel image')
plt.axis('off');

Canny 算子用于边缘检测

from skimage.feature import canny
from scipy import ndimage as ndi
img_edges = canny(img)
img_filled = ndi.binary_fill_holes(img_edges)

# Plot
plt.figure(figsize=(18, 12))
plt.subplot(121)
plt.imshow(img_edges, 'gray')
plt.subplot(122)
plt.imshow(img_filled, 'gray')

2. Pytorch基础

2.1 基本数据Tensor

Tensor是Pytorch中的基本操作对象,是存储和变换数据的主要工具。与NumPy的ndarrays非常类似,不过Tensor还支持GPU运算。Tensor有多种创建方法,如基础的Tensor(),还有多种与NumPy十分类似的方法,如ones()、eye()、zeros()和randn()等,其中PyTorch中默认的数据类型是 torch.FloatTensor。

a = torch.Tensor(2, 2)

# Tensor()与tensor的区别在于Tensor返回的数据类型是默认的,而tensor类型根据数据进行推断
# 参数可以为Python的list、NumPy的ndarray
x = torch.tensor((2, 3, 4, 9))

# 使用ones()函数,所有元素均为1
x2 = torch.ones((2, 3))

# 通过现有的Tensor来创建,此方法会默认重用输入Tensor的一些属性,例如数据类型,除非自定义数据类型
# 使用dtype来定义数据类型
x3 = torch.zeros_like(x2, dtype=torch.long)

# 使用randn()函数,生成标准分布的随机数矩阵
x4 = torch.randn(3, 4)

# 使用arange(start, end, step)函数,表示从start到end,间距为step,一维向量
v = torch.arange(1,4)

# 使用linspace(start, end, steps)函数,表示从start到end,一共steps份,一维向量
x5 = torch.linspace(10, 20, 15)

# 查看Tensor中总的元素个数
a.numel()

# 通过shape或者size()来获取Tensor的形状或维度
print(x.size())
print(x.shape)

2.2 Tensor的各种操作

索引

可以使用类似NumPy的索引操作来访问Tensor的一部分,但是索引出来的结果与原数据共享内存,即修改一个,另一个会跟着修改。

Input:
m = torch.tensor([[2, 3, 4],[5, 6, 7]])
print(m[:,1])
print(m[0])
print(m[0][1])

Output:
tensor([3, 6])
tensor([2, 3, 4])
tensor(3)

# 选择符合条件的元素并返回
Input:
a = torch.tensor([[1, 2], [3, 4]])
print(a > 2)
ind = a > 2
print(a[ind])

OUtput:
tensor([[False, False],
        [ True,  True]])
tensor([3, 4])

改变形状

变形操作则是指改变Tensor的维度,有四类不同的基本操作。

变形操作 功能
view()、resize()、reshape() 调整形状同时元素总数相同
transpose()、permute() 各维度之间的变换
squeeze()、unsqueeze() 处理size为1的维度
expand()、expand_as() 复制元素来扩展维度
# 将数据的第二维度与第三维度进行变换

Input:
a = torch.randn((2, 3, 2))
print(a)
res = a.transpose(1, 2)
print(res)

OUtput
tensor([[[-1.6705, -1.0614],
         [ 0.9338,  1.2305],
         [ 0.3439, -0.6650]],

        [[-0.9470, -0.8022],
         [ 0.8864, -0.6687],
         [-0.8743,  2.5200]]])
tensor([[[-1.6705,  0.9338,  0.3439],
         [-1.0614,  1.2305, -0.6650]],

        [[-0.9470,  0.8864, -0.8743],
         [-0.8022, -0.6687,  2.5200]]])

归并运算

归并运算可以沿着某一维度进行指定操作或者逐个元素进行操作。

# 按照第0维即按行选取最大值,将每一列的最大值选取出来
# 返回符合操作的数值及其下标
Input:
test = torch.tensor([[1, 8, 3],[4, 5,6]])
num, ind = torch.max(test, 0)
print(num,ind)

Output:

tensor([4, 8, 6]) tensor([1, 0, 1])

数据类型转换

  • GPU上Tensor数据与CPU上Tensor数据互相转换,可以通过data.cpu()data.cuda()data.to(device)实现。
# 将CPU上的Tensor数据转换为GPU 方法一

Input:
device = torch.device('cuda:0')
t1 = torch.tensor([1, 2, 3])
print(t1.type())

t1 = t1.to(device)
print(t1.type())

Output:
torch.LongTensor
torch.cuda.LongTensor

# 方法二
t1 = t1.cuda()

# 将GPU上的Tensor数据转换为CPU
t2 = t1.cpu()
  • Tensor与Numpy Array之间的转换,CPU上Tensor数据可以通过data.numpy()进行转换,但是CPU上Tensor数据要先转换为CPU后在进行Numpy类型转换。
# 将GPU上Tensor数据转换为Numpy类型
t2 = t1.cpu().numpy()

#  将Numpy类型转换为Tensor数据
Input:
t3 = torch.from_numpy(t2)
t3.type()

Output:
torch.LongTensor
  • Tensor的基本类型t转换,可以通过torch.tensor(data, dtype=)来实现

3. 螺旋数据分类

3.1 加载相应的包

!wget https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/plot_lib.py
import torch
import random
import math
from torch import nn, optim
from IPython import display
from plot_lib import plot_data, plot_model, set_default

3.2 生成数据集

由于PyTorch为数据在GPU上运行提供了便利的操作。因此可以在GPU进行运算,torch.cuda.is_available()可以用来判断当前环境下GPU是否可用,使用tensor.to(device)可以将数据转移到GPU上。

# 将所有最开始读取数据时的tensor变量copy一份到device所指定的GPU上去,之后的运算都在GPU上进行。
# 这句话需要写的次数等于需要保存GPU上的tensor变量的个数;
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('device',device)

seed=1234
random.seed(seed)
torch.manual_seed(seed)

构造一个简单的训练数据集,训练数据集样本数为1000,特征数为2,样本标签数为3

N = 1000  # 每类样本的数量
D = 2  # 每个样本的特征维度
C = 3  # 样本的类别
H = 100  # 神经网络里隐层单元的数量

X = torch.zeros(N*C, D).to(device)
Y = torch.zeros(N*C, dtype=torch.long).to(device)

for c in range(C):
    index = 0
    t = torch.linspace(0, 1, N) # 在[0,1]间均匀的取10000个数,赋给t
    # 下面的代码不用理解太多,总之是根据公式计算出三类样本(可以构成螺旋形)
    # torch.randn(N) 是得到 N 个均值为0,方差为 1 的一组随机数,注意要和 rand 区分开
    inner_var = torch.linspace( (2*math.pi/C)*c, (2*math.pi/C)*(2+c), N) + torch.randn(N) * 0.2
    
    # 每个样本的(x,y)坐标都保存在 X 里
    # Y 里存储的是样本的类别,分别为 [0, 1, 2]
    for ix in range(N * c, N * (c + 1)):
        X[ix] = t[index] * torch.FloatTensor((math.sin(inner_var[index]), math.cos(inner_var[index])))
        Y[ix] = c
        index += 1

print("Shapes:")
print("X:", X.size())
print("Y:", Y.size())

数据如图所示:

3.3 定义模型

torch.nn.Sequential是一个Sequential容器,模块将按照在传入构造器的顺序依次被添加到计算图中执行,当模型中只是简单的前馈网络时,即上一层的输出直接作为下一层的输入,可以采用nn.Sequential()模块来快速搭建模型。

nn.optim中包含了各种常见的优化算法,包括随机梯度下降算法SGD、Adam等。SGD容易陷入局部最优难以跳出,因此相对于SGD,Adam通常会有更好的效果。

# 网络模块
# # 每一个线性模型都包含 weight 和 bias
model = nn.Sequential(
    nn.Linear(D, H),
    nn.Linear(H, C)
)
model.to(device)


# 损失函数的定义,为交叉熵(cross entropy loss)损失函数
loss_dev = torch.nn.CrossEntropyLoss()

# 梯度下降的学习率
learnrate = 1e-3
decay = 1e-5

# 优化参数定义,为随机梯度下降(stochastic gradient descent)
optimize = torch.optim.SGD(model.parameters(), lr=learnrate, weight_decay=decay)

3.4 模型训练

for t in range(1000):
  # 模型的预测输出
  y_hat = model(X)
  
  # 求得预测输出的结果值及其下标
  score, predict = torch.max(y_hat, 1)
  
  # 模型预测的准确率和损失
  acc = (Y==predict).sum().float()/len(Y)
  loss = loss_dev(y_hat, Y)
  display.clear_output(wait=True)
  if t % 100 == 0:
    print('epoch:%d loss:%.6f accuracy:%.3f'%(t, loss.item(), acc))
  
  # 反向传播前把梯度置 0 
  optimize.zero_grad()
  # # 反向传播优化 
  loss.backward()
  # 通过step函数来迭代模型参数
  optimize.step()

最终运行结果为:

epoch:900 loss:0.772457 accuracy:0.502

模型的结构为

Sequential(
  (0): Linear(in_features=2, out_features=100, bias=True)
  (1): Linear(in_features=100, out_features=3, bias=True)
)

我们从model获得需要的层,并访问其权重(weight)和偏差(bias

dense = model[0]
print(dense.weight.size())
print(dense.bias)

3.5 改变模型结构

由于之前的模型训练出来的结果不太理想,所以这次改变了模型的结构,增加了ReLu(),ReLu激活函数将模型从线性变成非线性,并将优化算法变为了Adam。

model = nn.Sequential(
    nn.Linear(D, H),
    nn.ReLU(),
    nn.Linear(H, C)
)
model.to(device)
loss_dev = torch.nn.CrossEntropyLoss()
optimize = torch.optim.Adam(model.parameters(), lr=learnrate, weight_decay=decay)

最终运行结果为:

epoch:900 loss:0.220317 accuracy:0.925

模型的结构为

Sequential(
  (0): Linear(in_features=2, out_features=100, bias=True)
  (1): ReLU()
  (2): Linear(in_features=100, out_features=3, bias=True)
)

4. 回归分析

4.1 加载相应的包

import torch
import math
import random
from torch import nn, optim
from matplotlib import pyplot as plt
from IPython import display
from plot_lib import plot_data, plot_model, set_default

4.2 生成数据集

N = 1000  # 每类样本的数量
D = 1  # 每个样本的特征维度
C = 1  # 类别数
H = 100  # 隐层的神经元数量

X = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1).to(device)
y = X.pow(3) + 0.3 * torch.rand(X.size()).to(device)

展示数据:

plt.figure(figsize=(6, 6))
plt.scatter(X.cpu().numpy(), y.cpu().numpy())
plt.axis('equal')
plt.show()

4.3 定义模型与训练

模型与训练过程与螺旋数据分析一样

训练结果:

epoch:900 loss:0.037766

画图展示训练模型结果:

plt.figure(figsize=(6, 6))
plt.scatter(X.cpu().numpy(), y.cpu().numpy())
plt.plot(X.cpu().numpy(), y_hat.data.cpu().numpy(), 'r')
plt.axis('equal')
plt.show()

4.5 改变模型结构

第一种改变

在模型中加入了ReLu(),并将优化算法变为了Adam。

model = nn.Sequential(
    nn.Linear(D, H),
    nn.ReLU(),
    nn.Linear(H, C)   
)
model.to(device)

训练结果:

epoch:900 loss:0.006540

第二种改变

在模型中加入了Tanh(),并将优化算法变为了Adam。

model = nn.Sequential(
    nn.Linear(D, H),
    nn.Tanh(),
    nn.Linear(H, C)   
)
model.to(device)

训练结果:

epoch:900 loss:0.009457

画图展示两种训练模型结果:

猜你喜欢

转载自www.cnblogs.com/lixinyue0118/p/13378191.html