• pytorch实现吴恩达老师深度学习课程课后编程作业 课程1 - 神经网络和深度学习 要做的事：使用pytorch搭建神经网络 import torch.nn as nn import torch import numpy as np #我们随机创建一组训练数据，100个具有...
用pytorch实现吴恩达老师深度学习课程课后编程作业
课程1 - 神经网络和深度学习 要做的事：使用pytorch搭建神经网络
import torch.nn as nn
import torch
import numpy as np
#我们随机创建一组训练数据，100个具有500维特征的数据
M=100
features=500
train_x = torch.randn(M, features)
train_y = torch.randint(0, 2, [M,1]).float()         #数据集统一是float类型

#建立网络
in_put=features
Hidden1=10
Hidden2=5
out_put=1         #这里是一个输入层，两个隐藏层和一个输出层。

model = torch.nn.Sequential(
torch.nn.Linear(in_put, Hidden1, bias=True),
torch.nn.Sigmoid(),
torch.nn.Linear(Hidden1, Hidden2, bias=True),
torch.nn.ReLU(),
torch.nn.Linear(Hidden2, out_put, bias=True),
torch.nn.Sigmoid(),
)

#模型建好以后要定义损失函数和模型优化方法，torch包含多种方法，可自行百度
iter_n=1000       #迭代次数
learn_rate=1e-2
loss_fn = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learn_rate)

#开始优化
for i in range(iter_n):
y_pred=model(train_x)            #一次训练后得到的结果
loss=loss_fn(y_pred,train_y)
print(i,loss.item())
loss.backward()
optimizer.step()

展开全文
• import torch import numpy as np # import torch.utils.data as datasets from torch.utils.data import Dataset from torch.utils.data import DataLoader from torch import nn from scipy.io import loadmat ...
import torch
import numpy as np
# import torch.utils.data as datasets
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
from torch import nn
from scipy.io import loadmat

learning_rate = 0.01
epoch_num = 5

print(data.keys())
print(len(data['X']),type(data['X']),data['X'].shape)
print(np.max(data['y']))

class ImagDataset(Dataset):
def __init__(self):
path = '/content/drive/MyDrive/colab/data/NUMIMG/ex4data1.mat'
self.data_x = torch.from_numpy(data['X']).float()
self.data_y = torch.from_numpy(data['y']).long().reshape(-1) - 1
self.sample_num = len(data['X'])
def __getitem__(self,index):
return self.data_x[index], self.data_y[index]
def __len__(self):
return self.sample_num

dataset = ImagDataset()

class NeuralNet(nn.Module):
def __init__(self):
super(NeuralNet, self).__init__()
self.linear1 = nn.Linear(400, 512)
self.relu = nn.ReLU()
self.linear2 = nn.Linear(512, 10)
def forward(self, x):
out = self.linear1(x)
out = self.relu(out)
out = self.linear2(out)
return out

model = NeuralNet()

loss_fc = nn.CrossEntropyLoss()
optim = torch.optim.Adam(model.parameters(), lr=learning_rate)

print(total_step)
for epoch in range(epoch_num):
for i, (images, labels) in enumerate(dataloader):
out = model(images)

# print(out.shape,labels.shape)
loss = loss_fc(out,labels)

loss.backward()
optim.step()

if (i + 1) % 32 == 0:
print('epoch [{}/{}] step [{}/{}] loss: {:.4f}'.format(epoch + 1, epoch_num, i + 1, total_step, loss.item()))

correct = 0
total = 0
for images,label in dataloader:
out = model(images)
val, pred = torch.max(out, 1)
total += images.shape[0]
correct += (pred == label).sum().item()
print('accuracy on {} test cases is {}%'.format(len(dataset), 100 * correct / len(dataset)))

里面有很多细节，比如在colab使用本地数据，并结合dataset和dataloader使用；dataset的一些细节问题
展开全文
• 要做的事： 1 参数初始化 2 正则化 参数初始化 #model[0].weight是第0层的w参数，其他层可同样的方法初始化参数，初始化在建好model后使用，怎样建model参考上一篇。 # 0-1之间均匀分布 torch.nn.init.uniform_...
要做的事： 1 参数初始化 2 正则化
参数初始化
#model[0].weight是第0层的w参数，其他层可同样的方法初始化参数，初始化在建好model后使用，怎样建model参考上一篇。
# 0-1之间均匀分布
torch.nn.init.uniform_(model[0].weight, a=0, b=1)
# 初始化为常数0.5
torch.nn.init.constant_(model[0].weight, 0.5)
# 正态分布
torch.nn.init.normal_(model[0].weight)


正则化
正则化是用来减小过拟合的方法，这里给出L2正则化方法和dropout方法
L2正则化
optimizer = torch.optim.Adam(model.parameters(), lr=learn_rate,weight_decay=0.01)
#这里的weight_decay=0.01相当于λ参数。

dropout方法
model=torch.nn.Sequential(
torch.nn.Linear(in_put,Hidden1,bias=True),
torch.nn.ReLU(),
torch.nn.Dropout(0.2),
torch.nn.Linear(Hidden1,Hidden2,bias=True),
torch.nn.ReLU(),
torch.nn.Dropout(0.2),
torch.nn.Linear(Hidden2,out_put,bias=True),
torch.nn.Sigmoid(),
)
#在每层后边加上torch.nn.Dropout(0.2)，0.2是随机架空该层20%神经元。

结合上篇看
展开全文
• 最后本地测试的图片来源于百度及这位大佬，他写了这份作业的...tensorflow实在有点用不来，所以入pytorch的坑了。。 以下文件的【下载地址】，提取码53lw C:. │ ex6 pytorch.py │ tf_utils.py │ ├─datase...

最后本地测试的图片来源于百度及这位大佬，他写了这份作业的Tensorflow版https://blog.csdn.net/u013733326/article/details/79971488

tensorflow看起来有点复杂，所以决定入坑pytorch，用了几天觉得pytorch挺香的。如果你知道张量是怎么回事可以跳过前面，直接去看一个神经网络怎么实现。

资料下载
工程文件的【下载地址】，提取码：v1wz

前提
本代码基于pytorch1.4.0版本实现【pytorch官网下载地址】【清华镜像下载方法】

Tensor（张量）是pytorch运算的一种基本数据类型，它与ndarry在numpy中的作用类似，不同的是，它可以使用GPU进行加速，torch提供了这两者自由转换的接口。这个代码没有涉及GPU加速的内容。
1- 导入pytorch库
import numpy as np
from matplotlib import pyplot as plt
from tf_utils import load_dataset
import torch
from torch import nn
from torch.utils.data import DataLoader, TensorDataset
# 测试本地图片时使用
import imageio
import cv2

固定全局随机种子
# 固定numpy随机种子
np.random.seed(1)
# 固定torch随机种子
torch.manual_seed(1)

torch有自己独立的随机种子，试过同样用seed1，它和numpy生成的随机数不同。
计算一个平方损失
可以看看pytorch是怎么计算下面这个损失的：

l

o

s

s

=

L

(

y

^

,

y

)

=

(

y

^

(

i

)

−

y

(

i

)

)

2

(1)

loss = \mathcal{L}(\hat{y}, y) = (\hat y^{(i)} - y^{(i)})^2 \tag{1}

y_hat = torch.tensor(36)                  # torch.Tensor是torch.FloatTensor的简称，可用.int()或.to(int)强制转换为整型
y = torch.tensor(39)                      # 而torch.tensor根据传入数据的类型推断Tensor的数据类型
loss = (y - y_hat)**2
# 张量转换成np数组
loss = loss.numpy()
print(loss)

执行结果：
9

一个坑：最好给torch.Tensor()传入至少一维的数据，如torch.tensor([36])，否则会出现意想不到的结果，而torch.tensor()没有这个限制。
计算两个张量的对应元素相乘
a = torch.tensor(2)
b = torch.tensor(10)
# 对应元素相乘，使用.mul()或 * ，有broadcast机制
c = torch.mul(a, b).numpy()
print('c=', c)

执行结果：
c = 20

对比：pytorch的元素乘法使用torch.mul()或 * ，numpy使用np.multiply()或 * ，都有广播机制。
拓展：计算两个一维张量的内积 / 计算矩阵乘法
# 张量内积
d = torch.tensor([2, 2])
e = torch.tensor([1, 4])
print('张量内积：', torch.dot(d, e).numpy())
# 矩阵乘法
f = torch.tensor([[1], [4]])
print('矩阵乘积:', torch.matmul(d, f).numpy())

执行结果：
张量内积： 10
矩阵乘积: [10]

对比：np.dot()可以用于矩阵运算，但torch.dot()只能用于一维张量计算内积（用于二维运算则报错），torch中与np.dot()作用相对的其实是torch.matmul(）。

1.1 - 线性函数
试试用pytorch实现一个线性函数——𝑌=𝑊𝑋+𝑏 , 𝑊和𝑋是随机矩阵，b是一个随机向量。其中，W的维度是(4,3)，X的维度是(3,1)，b的维度是(4,1)。
def linear_function():
# X, Y, b都初始化为标准正态分布随机数组
X = torch.from_numpy(np.random.randn(3, 1))             # 由于torch和numpy库的随机种子不同，用np取随机数再转为张量
W = torch.from_numpy(np.random.randn(4, 3))             # 如果用torch，则 torch.randn(4, 3)
b = torch.from_numpy(np.random.randn(4, 1))
Y = torch.add(torch.matmul(W, X), b)

return Y.numpy()
print(linear_function())

执行结果：
[[-2.15657382]
[ 2.95891446]
[-1.08926781]
[-0.84538042]]

1.2 - sigmoid函数
同上，实现一个sigmoid函数试试吧：
def sigmoid(z):
z = torch.tensor(z, dtype=torch.float32)                  # torch.sigmoid不接受长整型
sig = torch.sigmoid(z)
return sig
print('sigmoid(0)= ', str(sigmoid(0).numpy()))
print('sigmoid(12)= ',  str(sigmoid(12).numpy()))

执行结果：
sigmoid(0) = 0.5
sigmoid(12) = 0.9999938

小数位比较长，问题不大。
1.3 - 二值交叉熵
我们可以使用一个内置函数计算二值交叉熵成本，而不需要编写代码来计算这个的函数：

J

=

−

1

m

∑

i

=

1

m

(

y

(

i

)

log

⁡

a

[

2

]

(

i

)

+

(

1

−

y

(

i

)

)

log

⁡

(

1

−

a

[

2

]

(

i

)

)

)

(2)

J = - \frac{1}{m} \sum_{i = 1}^m \large ( \small y^{(i)} \log a^{ [2] (i)} + (1-y^{(i)})\log (1-a^{ [2] (i)} )\large )\small\tag{2}

def cost(logits, labels):
# 为了和课堂代码一致，再计算一层sigmoid
input = sigmoid(logits.numpy())
# 计算成本
# BCELoss是基于sigmoid的函数，reduction默认='mean'，返回成本均值；reduction='none'，则不进行取均值的计算，返回每个样本的成本
loss = nn.BCELoss(reduction='none')
cost = loss(input, labels)
return cost
logits = sigmoid(np.array([0.2, 0.4, 0.7, 0.9]))
labels = torch.Tensor([0, 0, 1, 1])
cost = cost(logits, labels)
print('cost=', cost.numpy())

执行结果：
cost= [1.0053871  1.0366408  0.41385436 0.39956608]

Andrew用tensorflow计算的答案：  有末位差别，可能是框架保留小数的机制不同导致的
1.4 - 独热编码
在torch里似乎不是太用得到，因为计算多分类交叉熵时，torch内置的成本函数会自动将y转换成独热编码，不需要我们手动转换，在后面搭神经网络时可以看到。这里为了完整性还是写了一下。
# 独热编码 torch在计算交叉熵时会自动将label转独热编码
# （不过计算MSELoss时需要手动转换）
def one_hot(label, C):
"""
:param label: 张量，y标签
:param C: int，类别数
:return: 独热矩阵，与y标签数组对应
"""
# 这里的y是一维，只有一个维度，=m
m = labels.shape[0]
# 将独热矩阵初始化为全0，torch.zeros与np.zeros类似，torch建立的是tensor类型，而np建立的是ndarry类型
one_hot_matrix = torch.zeros(size=(C, m))
# 每列为一个样本，历遍列 比如该样本的标签为0，则在第一行标1，其他行标0
for l in range(m):
one_hot_matrix[label[l]][l] = 1
return one_hot_matrix.numpy()
# 测试
labels = np.array([1,2,3,0,2,1])
one_hot = one_hot(labels, C=4)
print('one_hot = ' + str(one_hot))

执行结果：
one_hot = [[0. 0. 0. 1. 0. 0.]
[1. 0. 0. 0. 0. 1.]
[0. 1. 0. 0. 1. 0.]
[0. 0. 1. 0. 0. 0.]]

1.5 - 用数字1来初始化
torch和numpy在创建全0、全1矩阵上的思路是类似的，简单地传入一个形状参数即可，不同的是，np创建的是ndarry类型，torch创建的是tensor类型。
def ones(shape):
ones_ = torch.ones(shape)
return ones_.numpy()
print('ones = ' + str(ones([3])))

一个坑： 在torch中尽量不要让变量和创建的方法用相同的命名，否则可能会出一些意想不到的错误。
执行结果：
ones = [1. 1. 1.]


2 - 使用pytorch构建第一个神经网络
（机翻英语：   一天下午，我们和一些朋友决定教我们的电脑破译手语。我们花了几个小时在白色的墙壁前拍照，于是就有了了以下数据集。现在，你的任务是建立一个算法，使有语音障碍的人与不懂手语的人交流。一天下午，我们和一些朋友决定教我们的电脑破译手语。我们花了几个小时在一堵白墙前拍照，得到了以下数据集。现在你的工作是建立一种算法，帮助语言障碍人士和不懂手语的人进行交流。 训练集:1080张图片(64×64像素)，这些符号表示0到5之间的数字(每个数字180张图片)。 测试集:120张图片(64×64像素)，这些符号表示0到5之间的数字(每个数字20张图片)。   注意，这是sign数据集的一个子集。完整的数据集包含更多的符号。   下面是每个数字的例子。）
2.1 - 加载数据集
# 载入数据集
X_train_orig, Y_train_orig, X_test_orig, Y_test_orig, classes = load_dataset()

# 可视化一张图片
index = 0
plt.imshow(X_train_orig[index])
plt.show()
print('y = ', np.squeeze(Y_train_orig[:, index]))

# 展平数据集
X_train_flatten = X_train_orig.reshape(X_train_orig.shape[0], -1)
X_test_flatten = X_test_orig.reshape(X_test_orig.shape[0], -1)
# 归一化数据集
X_train = X_train_flatten/255
X_test = X_test_flatten/255
# 转置y
Y_train = Y_train_orig.T
Y_test = Y_test_orig.T

print('number of training examples = ' + str(X_train.shape[1]))
print('number of test examples = ' + str(X_test.shape[1]))
print('X_train shape: ' + str(X_train.shape))
print('Y_train shape: ' + str(Y_train.shape))
print('X_test shape: ' + str(X_test.shape))
print('Y_test shape: ' + str(Y_test.shape))

一张图片可视化的结果：
y =  5

说明这是一个数字5。
维度打印结果：
number of training examples = 12288
number of test examples = 12288
X_train shape: (1080, 12288)
Y_train shape: (1080, 1)
X_test shape: (120, 12288)
Y_test shape: (120, 1)

注意12288来自64×64×3。每个图像是正方形的，64×64像素，3是RGB颜色。这样就保证x，y的行数相同，传入数据接口就不会有问题了。
2.2 - 创建网络结构
这个网络的前向传播的方式为：LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SOFTMAX。在torch中搭建神经网络，只需将前向传播部分放在网络结构中。这里用的是快速搭建法，更一般地会用到module类。
# 创建网络结构
def nerual_net():
# 这里最后一层用的是LogSoftmax，它是Softmax取log的结果
# torch的Softmax->log->NLLLose(接下来计算成本的函数) == tf.nn.softmax_cross_entropy_with_logits == torch.nn.CrossEntropyLoss
net = nn.Sequential(
nn.Linear(12288, 25),
nn.ReLU(),
nn.Linear(25, 12),
nn.ReLU(),
nn.Linear(12, 6),
nn.LogSoftmax(dim=1)
)
return net
# 看一下创建的网络结构
net = nerual_net()
print(net)

执行结果：
Sequential(
(0): Linear(in_features=12288, out_features=25, bias=True)
(1): ReLU()
(2): Linear(in_features=25, out_features=12, bias=True)
(3): ReLU()
(4): Linear(in_features=12, out_features=6, bias=True)
(5): LogSoftmax()
)

0-5表示层索引，如果用module搭和这个相同的网络，这里print出来不同，层会以自己的函数命名，比如ReLU层的索引是ReLU。
2.3 - 初始化参数
这个函数对权重矩阵Wi使用Xaiver初始化，对偏置向量bi使用0初始化。（后面建立模型时未实际用到这个函数，因为未找到的原因，它后期收敛速度不及默认的初始化方法，测试准度63%左右，比Tensorflow版低8%，而默认初始化方法的测试准度达到87.5%）
def initialize_params(net):
for i in range(len(net)):
layer = net[i]
if isinstance(layer, nn.Linear):
# 对Wi使用Xaiver初始化
nn.init.xavier_uniform_(layer.weight, gain=nn.init.calculate_gain('relu'))
# 对bi使用0初始化
nn.init.constant_(layer.bias, 0)
print('W' + str(i//2+1) + ' = ' + str(layer.weight.dtype) + ' ' + str(layer.weight.shape))
print('b' + str(i//2+1) + ' = ' + str(layer.bias.dtype) + ' ' + str(layer.bias.shape))
return net
# 测试初始化参数
initialize_params(net)

执行结果：
W1 = torch.float32 torch.Size([25, 12288])
b1 = torch.float32 torch.Size([25])
W2 = torch.float32 torch.Size([12, 25])
b2 = torch.float32 torch.Size([12])
W3 = torch.float32 torch.Size([6, 12])
b3 = torch.float32 torch.Size([6])

2.4 - 创建数据接口
这个函数将X_train, Y_train打包，可以理解为打包成类似于小批量迭代器的形式，每历遍所有批次再进行下一轮迭代，它都会自动打乱数据集，使每个批次内的样本保持随机。
def data_loader(X_train, Y_train, batch_size=32):
train_db = TensorDataset(torch.from_numpy(X_train).float(), torch.squeeze(torch.from_numpy(Y_train)))
# shuffle=True，则每次历遍批量后重新打乱顺序

在封装的模型中调用这个函数
2.5 - 封装模型
模型运行大概需要5-8分钟。
# 封装模型
def model(X_train, Y_train, X_test, Y_test, lr=0.0001, epochs=1500, batch_size=32, print_cost=True, is_plot=True):
# 载入数据
# 创建网络结构
net = nerual_net()
# 指定成本函数
cost_func = nn.NLLLoss()
optimizer = torch.optim.Adam(net.parameters(), lr=lr, betas=(0.9, 0.999))
# 保存每次迭代cost的列表
costs = []
# 批次数量
m = X_train.shape[0]
num_batch = m / batch_size

# 参数初始化 torch有默认的初始化方法，这里用Xaiver初始化效果不如默认，也不如tensorflow的示例代码，未找到原因，因此没用Xaiver初始化
# 想实验Xaiver初始化，取消注释下一行即可
# net = initialize_params(net)
# 迭代
for epoch in range(epochs):
# 历遍批次
epoch_cost = 0
for step, (batch_x, batch_y) in enumerate(train_loader):
# 前向传播
output = net(batch_x)
# 计算成本
cost = cost_func(output, torch.squeeze(batch_y))
epoch_cost += cost.data.numpy() / num_batch
# 梯度归零 backward()会在每次调用时累加梯度，不清零会干扰下一个批次计算梯度
# 反向传播
cost.backward()
# 更新参数
optimizer.step()
if print_cost and epoch % 5 == 0:
costs.append(epoch_cost)
if epoch % 100 == 0:
print('Cost after epoch %i : %f' % (epoch, epoch_cost))

# 画学习曲线
if is_plot:
plt.plot(costs)
plt.xlabel('iterations per 5')
plt.ylabel('cost')
plt.show()

# 保存学习后的参数
torch.save(net.state_dict(), 'net_params.pkl')
print('参数已保存到本地pkl文件。')

# # 计算训练集预测的结果
output_train = net(torch.from_numpy(X_train).float())
pred_Y_train = torch.max(output_train, dim=1)[1].data.numpy()
# 计算测试集预测的结果
output_test = net(torch.from_numpy(X_test).float())
pred_Y_test = torch.max(output_test, dim=1)[1].data.numpy()
# 训练集准确率
print('Train Accuracy: %.2f %%' % float(np.sum(np.squeeze(Y_train) == pred_Y_train)/m*100))
# 测试集准确率
print('Test Accuracy: %.2f %%' % float(np.sum(np.squeeze(Y_test) == pred_Y_test)/X_test.shape[0]*100))
return net

执行结果：
Cost after epoch 0 : 1.813178
Cost after epoch 100 : 0.888610
Cost after epoch 200 : 0.574326
Cost after epoch 300 : 0.428579
Cost after epoch 400 : 0.302992
Cost after epoch 500 : 0.207265
Cost after epoch 600 : 0.141452
Cost after epoch 700 : 0.105820
Cost after epoch 800 : 0.053666
Cost after epoch 900 : 0.033947
Cost after epoch 1000 : 0.020257
Cost after epoch 1100 : 0.011837
Cost after epoch 1200 : 0.006753
Cost after epoch 1300 : 0.003172
Cost after epoch 1400 : 0.001771
参数已保存到本地pkl文件。
Train Accuracy: 100.00 %
Test Accuracy: 87.50 %

使用torch默认的初始化方法，训练准确率达到100%，测试准确率达到87.5%，挺不错的结果。如果用Xaiver初始化则收敛得比较慢，原因还没找到，也许以后会回来补充。   （机翻英语：   你的模型看起来足够大，可以很好地适应训练集。然而，考虑到训练和测试准确性之间的差异，您可以尝试添加L2或dropout正则化来减少过拟合。）
2.6 - 测试本地的图片（选做）
用了大佬的自拍的手势图片和不知名的百度图片来做这个测试，出处见文章最开头。
def test_local_picture(img_path, y, trained_net, num_px=64):
# 读取一张图片
# 改变图像至指定尺寸。只保留resize()输出的前3列，前3列为rgb通道数值；第4列为固定值255，去掉
image_cut = cv2.resize(image, dsize=(num_px, num_px))[:, :, :3].reshape(1, -1)
# 归一化图像数据
x_test = image_cut/255
# 预测分类
y_test = np.array(y).reshape(1, 1)
output = trained_net(torch.from_numpy(x_test).float())
y_pred = torch.max(output, dim=1)[1].data.numpy()
y_pred = int(np.squeeze(y_pred))
# 打印结果
print('y  = %i, predicted % i. % s' % (y, y_pred, ('Correct.' if y == y_pred else 'Wrong.')))

# 测试不同的数字
trained_net = nerual_net()
IMG_PATH = 'datasets/test_images/%i.png'
for i in range(6):
img_path = IMG_PATH % i
test_local_picture(img_path, i, trained_net)


执行结果：
y  = 0, predicted  0. Correct.
y  = 1, predicted  1. Correct.
y  = 2, predicted  2. Correct.
y  = 3, predicted  2. Wrong.
y  = 4, predicted  2. Wrong.
y  = 5, predicted  5. Correct.

6中4，看起来还有很大的优化空间，3和4的手势图都被模型判断为数字2了

只要有numpy做基础，pytorch还是挺好理解的。希望这篇东西对你也有帮助。另外，推荐B站莫烦的pytorch教学视频，非常详细。
展开全文

...