1. 程式人生 > >MXNET:監督學習

MXNET:監督學習

激活 out 權重 scalar utf-8 註意 param puts 均值

線性回歸

給定一個數據點集合 X 和對應的目標值 y,線性模型的目標就是找到一條使用向量 w 和位移 b
描述的線,來盡可能地近似每個樣本X[i] 和 y[i]。

數學公式表示為\(\hat{y}=Xw+b\)

目標函數是最小化所有點的平方誤差 \(\sum_{i=1}^{n} (\hat{y_i}-y_i)^2\)

?個神經?絡就是?個由節點(神經元)和有向邊組成的集合。我們? 般把?些節點組成層,每?層先從下??層的節點獲取輸?,然後輸出給上?的層使?。要計算? 個節點值,我們需要將輸?節點值做加權和(權數值即w),然後再加上?個激活函數(activation function)。這裏的激活函數是\(f(x)=x\)

創建數據集: \(y=2*x[0] - 3.4*x[1] + 4.2 +noise\)

# -*- coding: utf-8 -*-
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 'dataset'

import matplotlib.pyplot as plt
plt.scatter(X[:, 1].asnumpy(),y.asnumpy())
plt.show()

當我們開始訓練神經?絡的時候,我們需要不斷讀取數據塊。這?我們定義?個函數它每次返回 batch_size 個隨機的樣本和對應的?標。

import random
batch_size = 10
def data_iter():
    # 產??個隨機索引
    idx = list(range(num_examples))
    random.shuffle(idx)
    for i in range(0, num_examples, batch_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)

隨機初始化模型參數,之後訓練時我們需要對這些參數求導來更新它們的值,使損失盡量減小;因此我們需要創建它們的梯度。

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

定義優化方案,我們這?通過隨機梯度下降來求解。每?步,我們將模型參數沿著梯度的反?向走特定距離,這個距離?般叫學習率(learning rate)lr

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

現在我們可以開始訓練了。訓練通常需要叠代數據數次,在這?使? epochs表?叠代總次數; ?次叠代中,我們每次隨機讀取固定數個數據點,計算梯度並更新模型參數。

epochs = 5
learning_rate = .001
niter = 0
moving_loss = 0
smoothing_constant = .01
# 訓練
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()
        # 記錄每讀取?個數據點後,損失的移動平均值的變化;
        niter +=1
        curr_loss = nd.mean(loss).asscalar()
        moving_loss = (1 - smoothing_constant) * moving_loss + (smoothing_constant * curr_loss)
        if (niter + 1) % 100 == 0:
            print("Epoch %d, batch %d. Average loss: %f" % (
                epochs, niter, moving_loss))
print(params)
# output
[[ 1.99952257]
 [-3.39969802]]
<NDArray 2x1 @cpu(0)>, 
[ 4.19949913]
<NDArray 1 @cpu(0)>

線性回歸-使用Gluon

這裏我們將使用MXNet提供的Gluon接口更方便地實現線性回歸的訓練。

首先生成數據集

num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
features = nd.random_normal(scale=1, shape=(num_examples, num_inputs))
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += nd.random_normal(scale=0.01, shape=labels.shape)

讀取數據,使用Gluon提供的data模塊來讀取數據。在每一次叠代中,我們將隨機讀取包含10個數據樣本的小批量。

from mxnet.gluon import data as gdata
batch_size = 10
dataset = gdata.ArrayDataset(features, labels)
data_iter = gdata.DataLoader(dataset, batch_size, shuffle=True)

在前面我們需要定義模型參數,並使用它們一步步描述模型是怎樣計算的。當模型結構變得更復雜時,這些步驟將變得更加繁瑣。其實,Gluon提供了大量預定義的層,這使我們只需關註使用哪些層來構造模型。
首先,導入nn模塊。我們先定義一個模型變量net,它是一個Sequential實例。在Gluon中,Sequential實例可以看做是一個串聯各個層的容器。在構造模型時,我們在該容器中依次添加層。當給定輸入數據時,容器中的每一層將依次計算並將輸出作為下一層的輸入。
線性回歸的輸出層又叫全連接層。在Gluon中,全連接層是一個Dense實例。我們定義該層輸出個數為1。

from mxnet.gluon import nn
net = nn.Sequential()
net.add(nn.Dense(1))

值得一提的是,在Gluon中我們無需指定每一層輸入的形狀,例如線性回歸的輸入個數。當模型看見數據時,例如後面執行net(X)時,模型將自動推斷出每一層的輸入個數。

初始化模型參數,從MXNet中導入init模塊,並通過init.Normal(sigma=0.01)指定權重參數每個元素將在初始化時隨機采樣於均值為0標準差為0.01的正態分布。偏差參數全部元素初始化為零。

from mxnet import init
net.initialize(init.Normal(sigma=0.01))

定義損失函數,從gluon引入loss模塊

from mxnet.gluon import loss as gloss
loss = gloss.L2Loss()

定義優化算法,在導入Gluon後,我們可以創建一個Trainer實例,並且將模型參數傳遞給它。

from mxnet.gluon import Trainer
trainer = Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.03})

訓練模型,我們通過調用step函數來叠代模型參數。由於變量l是batch_size維的NDArray,執行l.backward()等價於l.sum().backward()。按照小批量隨機梯度下降的定義,我們在step函數中提供batch_size,以確保小批量隨機梯度是該批量中每個樣本梯度的平均。

num_epochs = 3
for epoch in range(1, num_epochs + 1):
    for X, y in data_iter:
        with autograd.record():
            l = loss(net(X), y)
        l.backward()
        trainer.step(batch_size)
    print("epoch %d, loss: %f"
          % (epoch, loss(net(features), labels).asnumpy().mean()))

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

可以從net獲得需要的層,並訪問其權重和位移。學到的和真實的參數很接近。

MXNET:監督學習