一、使用NDArray來處理數(shù)據(jù)
首先從 MXNet 導(dǎo)入ndarray
模塊吁脱。nd
是ndarray
的縮寫形式涯保。
from mxnet import ndarray as nd
然后創(chuàng)建3行4列的2d數(shù)組
nd.zeros((3,4))
創(chuàng)建隨機數(shù)組一死,元素服從均值0,方差1的正態(tài)分布羡微。
y=nd.random_normal(0,1,shape=(3,4))
數(shù)組的形狀
y.shape
數(shù)組的大小
y.size
操作符
加法:x + y
乘法:x * y
指數(shù)運算:nd.exp(y)
轉(zhuǎn)秩矩陣然后計算矩陣乘法:nd.dot(x,y.T)
廣播
當二元操作符左右兩邊ndarray形狀不一樣時谷饿,系統(tǒng)會嘗試將其復(fù)制到一個共同的形狀。例如a的第0維是3妈倔,b的第0維是1博投,那么a+b時將b沿著第0維復(fù)制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的轉(zhuǎn)換
ndarray可以很方便同numpy進行轉(zhuǎn)換
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從現(xiàn)在指向的實例轉(zhuǎn)到新建的實例上去:
x = nd.ones((3,4))
y = nd.ones((3,4))
before = id(y)
y = y + x
id(y) == before
也可以把結(jié)果通過[:]寫到一個之前開好的數(shù)組里:
z = nd.zeros_like(x)
before = id(z)
z[:] = x + y
id(z) == before
這里為x + y創(chuàng)建了臨時空間盯蝴,然后復(fù)制到z毅哗,更簡便的做法是使用操作符的全名版本中的out參數(shù):
nd.elemwise_add(x, y,out=z)
id(z) == before
總結(jié)
- NDArray 是 MXNet 中存儲和變換數(shù)據(jù)的主要工具。
- 我們可以輕松地對 NDArray 創(chuàng)建捧挺、運算虑绵、指定索引,并與 NumPy 之間相互變換闽烙。
- ndarray模塊提供一系列多維數(shù)組操作函數(shù)翅睛。所有函數(shù)列表可以參見NDArrayAPI文檔。
使用autograd來自動求導(dǎo)
MXnet提供autograd包來自動化求導(dǎo)過程
import mxnet.adarray as nd
import mxnet.autograd as ag
為變量賦上梯度
對函數(shù)f = 2 * (x**2)求關(guān)于x的導(dǎo)數(shù)鸣峭;
創(chuàng)建變量x宏所,并賦初值酥艳。
x = nd.array([[1, 2],[3, 4]])
當進行求導(dǎo)時摊溶,需要一個空間來存放x的導(dǎo)數(shù),這個可以通過attach_grad()來要求系統(tǒng)申請對應(yīng)的空間充石。
x.attch_grad()
下面定義f莫换;默認條件下,MXNet不會自動記錄和構(gòu)建用于求導(dǎo)的計算圖骤铃,我們需要使用autograd里的record()函數(shù)來現(xiàn)實要求MXNet記錄我們需要求導(dǎo)的程序拉岁。
with at.record():
y = x * 2
z = y * x
使用z.backward()來進行求導(dǎo);如果z不是一個標量惰爬,那么z.backward()等價于nd.sum(z).backward()
z.backward()
驗證
x.grad == 4 * x
對控制流求導(dǎo)
可以對python的控制流進行求導(dǎo)喊暖。
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求導(dǎo)。
a = nd.random_normal(shape=3)
a.attach_grad()
with ag.record():
c = f(a)
c.backward()
a.grad == c/a
小結(jié)
- MXNet 提供autograd包來自動化求導(dǎo)過程撕瞧。
- MXNet 的autograd包可以對一般的命令式程序進行求導(dǎo)陵叽。
線性回歸
創(chuàng)建數(shù)據(jù)集
使用如下方法來生成數(shù)據(jù)
y[i] = 2*X[i][0] - 3.4 * X[i][1] + 4.2 + noise
噪音服從均值0和方差0.1的正態(tài)分布。
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])
數(shù)據(jù)讀取
當我們開始訓(xùn)練神經(jīng)網(wǎng)絡(luò)的時候丛版,我們需要不斷讀取數(shù)據(jù)塊巩掺。這里我們定義一個函數(shù)它每次返回batch_size個隨機的樣本和對應(yīng)的目標。我們通過python的yield來構(gòu)造一個迭代器页畦。
import random
batch_size = 10
def data_iter():
#產(chǎn)生一個隨機索引
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)
下面代碼讀取第一個隨機數(shù)據(jù)塊
for data, label in data_iter():
print(data, label)
break
初始化模型參數(shù)
下面我們隨機初始化模型參數(shù)
w = nd.random_normal(shape=(num_inputs, 1))
b = nd.zeros((1,))
params = [w, b]
之后訓(xùn)練時我們需要對這些參數(shù)求導(dǎo)來更新它們的值胖替,因此我們需要創(chuàng)建它們的梯度。
for param in params:
param.attach_grad()
定義模型
線性模型就是將輸入和模型做乘法再加上偏移:
def net(X):
return nd.dot(X, w) + b
損失函數(shù)
我們使用常見的平方誤差來衡量預(yù)測目標和真實目標之間的差距
def square_loss(yhat, y):
#注意這里我們把y變形成yhat的形狀來避免自動廣播
return (yhat - y.reshape(yhat.shape)) ** 2
優(yōu)化
這里通過隨機梯度下降來求解:
def SGD(params, lr):
for param in params:
param[:] = param - lr * param.grad
訓(xùn)練
訓(xùn)練通常需要迭代數(shù)據(jù)數(shù)次,一次迭代里独令,我們每次隨機讀取固定數(shù)個數(shù)據(jù)點端朵,計算梯度并更新模型參數(shù)。
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))
訓(xùn)練完成后可以比較學(xué)到的參數(shù)和真實參數(shù):
true_w, w
true_b, b
小結(jié)
可以看出燃箭,僅使用 NDArray 和autograd就可以很容易地實現(xiàn)一個模型逸月。
使用Gluon的線性回歸
創(chuàng)建數(shù)據(jù)集
生成同樣的數(shù)據(jù)集
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])
數(shù)據(jù)讀取
這里使用data模塊來讀取數(shù)據(jù)。
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提供大量的提前定制好的層遍膜,使得我們只需要主要關(guān)注使用哪些層來構(gòu)建模型碗硬。例如線性模型就是使用Dense層。
構(gòu)建模型最簡單的辦法是利用Sequential來所有層串起來瓢颅。首先我們定義一個空的模型:
net = gluon.nn.Sequential()
然后加入一個Dense層恩尾,唯一要定義的參數(shù)就是輸出節(jié)點的個數(shù),在線性模型里面是1.
net.add(gluon.nn.Dense(1))
(注意這里沒有定義這個層的輸入節(jié)點是多少挽懦,這個在之后真正給數(shù)據(jù)的時候系統(tǒng)會自動賦值翰意。之后會詳細解釋這個特性)
初始化模型參數(shù)
使用默認初始化方法
net.initialize()
損失函數(shù)
gluon提供了平方誤差函數(shù):
square_loss = gluon.loss.L2Loss()
優(yōu)化
創(chuàng)建一個Trainer的實例,并且將模型參數(shù)傳遞給它就行
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.1})
訓(xùn)練
不再調(diào)用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拿到需要的層信柿,然后訪問其權(quán)重和偏置冀偶。
dense = net[0]
true_w, dense.weight.data()
true_b, dense.bias.data()
小結(jié)
使用 Gluon 可以更簡潔地實現(xiàn)模型。
在 Gluon 中渔嚷,data模塊提供了有關(guān)數(shù)據(jù)處理的工具进鸠,nn模塊定義了大量神經(jīng)網(wǎng)絡(luò)的層,loss模塊定義了各種損失函數(shù)形病。
MXNet 的initializer模塊提供了模型參數(shù)初始化的各種方法客年。
從0開始的多類邏輯回歸
獲取數(shù)據(jù)
分類服飾
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))
數(shù)據(jù)讀取
直接使用DataLoader函數(shù)
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)
初始化模型參數(shù)
輸入向量的長度為2828,輸出向量長度為10漠吻,因此權(quán)重的大小為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]
為模型參數(shù)附上梯度:
for param in params:
param.attach_grad()
定義模型
這里使用softmax函數(shù)來將任意的輸入歸一成合法的概率值量瓜。
from mxnet import nd
def softmax(X):
exp = nd.exp(X)
# 假設(shè)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 系統(tǒng)自己判斷
return softmax(nd.dot(X.reshape((-1,num_inputs)), W) + b)
交叉熵損失函數(shù)
交叉熵損失函數(shù),將兩個概率分布的負交叉熵作為目標值耍共,最小化這個值等價于最大化這兩個概率的相似度烫饼。
def cross_entropy(yhat, y):
return - nd.pick(nd.log(yhat), y)
計算精度
給定一個概率輸出,我們將預(yù)測概率最高的那個類作為預(yù)測的類划提,然后通過筆記真實標號我們可以計算精度:
def accuracy(output, label):
return nd.mean(output.argmax(axis=1)==label).asscalar()
可以評估一個模型在這個數(shù)據(jù)上的精度枫弟。
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)
訓(xùn)練
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()
#對梯度做平均。這樣學(xué)習率會對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))
預(yù)測
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()))
小結(jié)
我們可以使用 Softmax 回歸做多類別分類鹏往。與訓(xùn)練線性回歸相比淡诗,你會發(fā)現(xiàn)訓(xùn)練 Softmax 回歸的步驟跟其非常相似:獲取并讀取數(shù)據(jù)骇塘、定義模型和損失函數(shù)并使用優(yōu)化算法訓(xùn)練模型。事實上韩容,絕大多數(shù)深度學(xué)習模型的訓(xùn)練都有著類似的步驟款违。