动手学深度学习笔记(一)

一、使用NDArray来处理数据

首先从 MXNet 导入ndarray模块。ndndarray的缩写形式。
from mxnet import ndarray as nd

然后创建3行4列的2d数组
nd.zeros((3,4))

创建随机数组,元素服从均值0,方差1的正态分布。
y=nd.random_normal(0,1,shape=(3,4))

数组的形状
y.shape

数组的大小
y.size

操作符

加法:x + y
乘法:x * y
指数运算:nd.exp(y)
转秩矩阵然后计算矩阵乘法:nd.dot(x,y.T)

广播

当二元操作符左右两边ndarray形状不一样时,系统会尝试将其复制到一个共同的形状。例如a的第0维是3,b的第0维是1,那么a+b时将b沿着第0维复制3遍:

a = nd.arrange(3).reshape((3,1))
b = nd.arrange(2).reshape((1,2))

print('a:',a)
print('b:',b)
print('a+b',a+b)

与Numpy的转换

ndarray可以很方便同numpy进行转换

import numpy as np

X = np.ones((2,3))
Y = nd.array(X) #numpy->mxnet
Z = y.asnumpy(Y) #mxnet->bumpy

替换操作

y = x + y,会将y从现在指向的实例转到新建的实例上去:

x = nd.ones((3,4))
y = nd.ones((3,4))

before = id(y)
y = y + x

id(y) == before

也可以把结果通过[:]写到一个之前开好的数组里:

z = nd.zeros_like(x)
before = id(z)
z[:] = x + y

id(z) == before

这里为x + y创建了临时空间,然后复制到z,更简便的做法是使用操作符的全名版本中的out参数:

nd.elemwise_add(x, y,out=z)

id(z) == before

总结

  • NDArray 是 MXNet 中存储和变换数据的主要工具。
  • 我们可以轻松地对 NDArray 创建、运算、指定索引,并与 NumPy 之间相互变换。
  • ndarray模块提供一系列多维数组操作函数。所有函数列表可以参见NDArrayAPI文档。

使用autograd来自动求导

MXnet提供autograd包来自动化求导过程

import mxnet.adarray as nd
import mxnet.autograd as ag

为变量赋上梯度

对函数f = 2 * (x**2)求关于x的导数;

创建变量x,并赋初值。

x = nd.array([[1, 2],[3, 4]])

当进行求导时,需要一个空间来存放x的导数,这个可以通过attach_grad()来要求系统申请对应的空间。

x.attch_grad()

下面定义f;默认条件下,MXNet不会自动记录和构建用于求导的计算图,我们需要使用autograd里的record()函数来现实要求MXNet记录我们需要求导的程序。

with at.record():
    y = x * 2
    z = y * x

使用z.backward()来进行求导;如果z不是一个标量,那么z.backward()等价于nd.sum(z).backward()

z.backward()

验证

x.grad == 4 * x

对控制流求导

可以对python的控制流进行求导。

def f(a):
    b = a * 2
    while nd.norm(b).asscalar() > 0:
        b = b * 2
    if nd.sum(b).asscalar()>0:
        c = b
    else:
        c = 100 * b
    return c

依旧可以用record记录和backward求导。

a = nd.random_normal(shape=3)
a.attach_grad()

with ag.record():
    c = f(a)

c.backward()
a.grad == c/a

小结

  • MXNet 提供autograd包来自动化求导过程。
  • MXNet 的autograd包可以对一般的命令式程序进行求导。

线性回归

创建数据集

使用如下方法来生成数据
y[i] = 2*X[i][0] - 3.4 * X[i][1] + 4.2 + noise
噪音服从均值0和方差0.1的正态分布。

from mxnet import ndarray as nd
from mxnet import autograd

num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2

X = nd.random_normal(shape=(num_examples, num_inputs))
y = true_w[0] * X[:,0] + true_w[1] * X[:,1] + true_b
y += .01 * nd.random_normal(shape=y.shape)

print(X[0], y[0])

数据读取

当我们开始训练神经网络的时候,我们需要不断读取数据块。这里我们定义一个函数它每次返回batch_size个随机的样本和对应的目标。我们通过python的yield来构造一个迭代器。

import random

batch_size = 10
def data_iter():
    #产生一个随机索引
    idx = list(range(num_examples))
    random.shuffle(idx)

    for i in range(0, num_examples, baych_size):
        j = nd.array(idx[i:min(i+batch_size,num_examples)])
        yield nd.take(X, j), nd.take(y, j)

下面代码读取第一个随机数据块

for data, label in data_iter():
    print(data, label)
    break

初始化模型参数

下面我们随机初始化模型参数

w = nd.random_normal(shape=(num_inputs, 1))
b = nd.zeros((1,))
params = [w, b]

之后训练时我们需要对这些参数求导来更新它们的值,因此我们需要创建它们的梯度。

for param in params:
    param.attach_grad()

定义模型

线性模型就是将输入和模型做乘法再加上偏移:

def net(X):
    return nd.dot(X, w)  + b

损失函数

我们使用常见的平方误差来衡量预测目标和真实目标之间的差距

def square_loss(yhat, y):
    #注意这里我们把y变形成yhat的形状来避免自动广播
    return (yhat - y.reshape(yhat.shape)) ** 2

优化

这里通过随机梯度下降来求解:

def SGD(params, lr):
    for param in params:
        param[:] = param - lr * param.grad

训练

训练通常需要迭代数据数次,一次迭代里,我们每次随机读取固定数个数据点,计算梯度并更新模型参数。

epochs = 5
learning_rate = .001

for e in range(epochs):
    total_loss = 0
    for data, label in data_iter():
        with autograd.record():
            output = net(data)
            loss = square_loss(output, label)
      loss.backward()
      SGD(params, learning_rate)
      total_loss += nd.sum(loss).asscalar()
    print(“Epoch %d, average loss: %f” % (e, total_loss/num_examples))

训练完成后可以比较学到的参数和真实参数:
true_w, w
true_b, b

小结
可以看出,仅使用 NDArray 和autograd就可以很容易地实现一个模型。

使用Gluon的线性回归

创建数据集

生成同样的数据集

from mxnet import ndarray as nd
from mxnet import autograd
from mxnet import gluon

num_inputs = 2
num_examples = 1000

true_w = [2, -3.4]
true_b = 4.2

X = nd.random_normal(shape=(num_examples, num_inputs))
y = true_w[0] * X[:,0] + true_w[1] * X[:,1] + true_b
y += .01 * nd.random_normal(shape=y.shape)

print(X[0], y[0])

数据读取

这里使用data模块来读取数据。

batch_size = 10
dataset = gluon.data.ArrayDataset(X,y)
data_iter = gluon.data.DataLoader(dataset, batch_size, shuffle=True)

读取和前面一致:

for data, label in data_iter:
    print(data, label)
    break

定义模型

gluon提供大量的提前定制好的层,使得我们只需要主要关注使用哪些层来构建模型。例如线性模型就是使用Dense层。

构建模型最简单的办法是利用Sequential来所有层串起来。首先我们定义一个空的模型:

net = gluon.nn.Sequential()

然后加入一个Dense层,唯一要定义的参数就是输出节点的个数,在线性模型里面是1.

net.add(gluon.nn.Dense(1))

(注意这里没有定义这个层的输入节点是多少,这个在之后真正给数据的时候系统会自动赋值。之后会详细解释这个特性)

初始化模型参数

使用默认初始化方法

net.initialize()

损失函数

gluon提供了平方误差函数:

square_loss = gluon.loss.L2Loss()

优化

创建一个Trainer的实例,并且将模型参数传递给它就行

trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.1})

训练

不再调用SGD而是trainer.step来更新模型

epochs = 5
batch_size = 0

for e in range(epochs):
    total_loss = 0
    for data, label in data_iter():
        with autograd.record():
            output = net(data)
            loss = square_loss(output, label)
      loss.backward()
      trainer.step(batch_size)
      total_loss += nd.sum(loss).asscalar()

    print("Epoch %d, average loss: %f"% (e, total_loss/num_examples))

先从net拿到需要的层,然后访问其权重和偏置。

dense = net[0]
true_w, dense.weight.data()
true_b, dense.bias.data()

小结
使用 Gluon 可以更简洁地实现模型。
在 Gluon 中,data模块提供了有关数据处理的工具,nn模块定义了大量神经网络的层,loss模块定义了各种损失函数。
MXNet 的initializer模块提供了模型参数初始化的各种方法。

从0开始的多类逻辑回归

获取数据

分类服饰

from mxnet import gluon
from mxnet import ndarray as nd

def transform(data, label):
    return data.astype('float32')/255, label.astype('float32')

mnist_train = gluon.data.vision.FashionMNIST(train=True, transform = transform)
mnist_test = gluon.data.vision.FashionMNIST(train=False, transform = transform)

打印样本的形状和标签

data, label = mnist_train[0]
(‘example shape: ‘, data.shape, 'label:', label)

样本图片显示

import matplotlib.pyplot as plt

def show_images(image):
    n = images.shape[0]
    _, figs = plt.subplots(1, n, figsize=(15, 15))

    for i in range(n):
        figs[i].imshow(images[i].reshape((28, 28)).asnumpy())
        figs[i].axes.get_xaxis().set_visible(False)
        figs[i].axes.get_yaxis().set_visible(False)

plt.show()

def get_text_labels(label):
    text_labels = [‘t-shirt', 'trouser', 'pullover', 'dress', ‘coat’,’sandal’, ’shirt’, ’sneaker’, bag', 'ankle boot']
    return [text_labels[int(i)] for i in label]

data, label = mnist_train[0:9]
show_images(data)
print(get_text_labels(label))

数据读取

直接使用DataLoader函数

batch_size = 256
train_data = gluon.data.DataLoader(mnist_train, batch_size, shuffle=True)
test_data = gluon.data.DataLoader(mnist_test, batch_size, shuffle=True)

初始化模型参数

输入向量的长度为2828,输出向量长度为10,因此权重的大小为78410:

num_inputs = 784
num_outputs = 10

W = nd.random_normal(shape=(num_inputs, num_outputs))
b = nd.random_normal(shape=num_outputs)
params = [W, b]

为模型参数附上梯度:


for param in params:

param.attach_grad()

定义模型

这里使用softmax函数来将任意的输入归一成合法的概率值。

from mxnet import nd

def softmax(X):
    exp = nd.exp(X)
    # 假设exp是矩阵,这里对行进行求和,并要求保留axis 1
    # 返回(nrows, 1)形状的矩阵
    partition = exp.sum(axis=1, keepdims=True)
    return exp / partiton

测试:

X = nd.random_normal(shape=(2,5))
x_prob = softmax(X)

print(X)
print(X_prob)
print(X_prob.sum(axis=1))

定义模型:

def net(X):
    # -1 系统自己判断
    return softmax(nd.dot(X.reshape((-1,num_inputs)), W) + b)

交叉熵损失函数

交叉熵损失函数,将两个概率分布的负交叉熵作为目标值,最小化这个值等价于最大化这两个概率的相似度。

def cross_entropy(yhat, y):
return - nd.pick(nd.log(yhat), y)

计算精度

给定一个概率输出,我们将预测概率最高的那个类作为预测的类,然后通过笔记真实标号我们可以计算精度:

def accuracy(output, label):
return nd.mean(output.argmax(axis=1)==label).asscalar()

可以评估一个模型在这个数据上的精度。

def evaluate_accuracy(data_iterator, net):
    acc = 0.

    for data, label in data_iterator:
        output = net(data)
        acc += accuracy(output, label)
        return acc / len(data_iterator)

尝试测试:

evaluate_accuracy(test_data, net)

训练

import sys
sys.path.append('..')
from utils import SGD
from mxnet import autograd

learning_rate = .1
for epoch in range(5):
    train_loss = 0.
    train_acc = 0.

for data, label in train_data:
    with autograd.record():
    output = net(data)
    loss = cross_entropy(output, label)
    loss.backward()
    #对梯度做平均。这样学习率会对batch size不那么敏感
    SGD(params, learning_rate/batch_size)
    train_loss += nd.mean(loss).asscalar()
    train_acc += accuracy(output, label)
    test_acc = evaluate_accuracy(test_data, net)

    print(“Epoch %d. Loss: %f, Test acc %f” % (epoch, train_loss/len(train_data),                                     \train_acc/len(train_data),test_acc/len(test_acc))

预测

data, label = mnist_test[0:9]
show_images(data)
print(‘true labels’)
print(get_text_labels(label))

predicted_labels = net(data).argmax(axis=1)
print(‘predicted labels’)
print(get_text_labels(predicted_labels.asnumpy()))

小结
我们可以使用 Softmax 回归做多类别分类。与训练线性回归相比,你会发现训练 Softmax 回归的步骤跟其非常相似:获取并读取数据、定义模型和损失函数并使用优化算法训练模型。事实上,绝大多数深度学习模型的训练都有着类似的步骤。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容