讓我們從經(jīng)典的線(xiàn)性回歸(Linear Regression [1])模型開(kāi)始這份教程。在這一章里,你將使用真實(shí)的數(shù)據(jù)集建立起一個(gè)房?jī)r(jià)預(yù)測(cè)模型,并且了解到機(jī)器學(xué)習(xí)中的若干重要概念。
本教程源代碼目錄在book/fit_a_line, 初次使用請(qǐng)參考PaddlePaddle安裝教程。
給定一個(gè)大小為$n$的數(shù)據(jù)集 ${{y_{i}, x_]{i1}, ..., x_{id}}}_{i=1}^{n}$,其中$x_{i1}, \ldots, x_{id}$是第$i$個(gè)樣本$d$個(gè)屬性上的取值,$y_i$是該樣本待預(yù)測(cè)的目標(biāo)。線(xiàn)性回歸模型假設(shè)目標(biāo)$y_i$可以被屬性間的線(xiàn)性組合描述,即
$$y_i = \omega_1x_{i1} + \omega_2x_{i2} + \ldots + \omega_dx_{id} + b, i=1,\ldots,n$$
例如,在我們將要建模的房?jī)r(jià)預(yù)測(cè)問(wèn)題里,$x_{ij}$是描述房子$i$的各種屬性(比如房間的個(gè)數(shù)、周?chē)鷮W(xué)校和醫(yī)院的個(gè)數(shù)、交通狀況等),而 $y_i$是房屋的價(jià)格。
初看起來(lái),這個(gè)假設(shè)實(shí)在過(guò)于簡(jiǎn)單了,變量間的真實(shí)關(guān)系很難是線(xiàn)性的。但由于線(xiàn)性回歸模型有形式簡(jiǎn)單和易于建模分析的優(yōu)點(diǎn),它在實(shí)際問(wèn)題中得到了大量的應(yīng)用。很多經(jīng)典的統(tǒng)計(jì)學(xué)習(xí)、機(jī)器學(xué)習(xí)書(shū)籍[2,3,4]也選擇對(duì)線(xiàn)性模型獨(dú)立成章重點(diǎn)講解。
我們使用從UCI Housing Data Set獲得的波士頓房?jī)r(jià)數(shù)據(jù)集進(jìn)行模型的訓(xùn)練和預(yù)測(cè)。下面的散點(diǎn)圖展示了使用模型對(duì)部分房屋價(jià)格進(jìn)行的預(yù)測(cè)。其中,每個(gè)點(diǎn)的橫坐標(biāo)表示同一類(lèi)房屋真實(shí)價(jià)格的中位數(shù),縱坐標(biāo)表示線(xiàn)性回歸模型根據(jù)特征預(yù)測(cè)的結(jié)果,當(dāng)二者值完全相等的時(shí)候就會(huì)落在虛線(xiàn)上。所以模型預(yù)測(cè)得越準(zhǔn)確,則點(diǎn)離虛線(xiàn)越近。
http://wiki.jikexueyuan.com/project/deep-learning/images/01-01.png" alt="png" />
圖1. 預(yù)測(cè)值 V.S. 真實(shí)值
在波士頓房?jī)r(jià)數(shù)據(jù)集中,和房屋相關(guān)的值共有14個(gè):前13個(gè)用來(lái)描述房屋相關(guān)的各種信息,即模型中的 $x_i$;最后一個(gè)值為我們要預(yù)測(cè)的該類(lèi)房屋價(jià)格的中位數(shù),即模型中的 $y_i$。因此,我們的模型就可以表示成:
$$\hat{Y} = \omega_1X_{1} + \omega_2X_{2} + \ldots + \omega_{13}X_{13} + b$$
$\hat{Y}$ 表示模型的預(yù)測(cè)結(jié)果,用來(lái)和真實(shí)值$Y$區(qū)分。模型要學(xué)習(xí)的參數(shù)即:$\omega_1, \ldots, \omega_{13}, b$。
建立模型后,我們需要給模型一個(gè)優(yōu)化目標(biāo),使得學(xué)到的參數(shù)能夠讓預(yù)測(cè)值$\hat{Y}$盡可能地接近真實(shí)值$Y$。這里我們引入損失函數(shù)(Loss Function,或Cost Function)這個(gè)概念。 輸入任意一個(gè)數(shù)據(jù)樣本的目標(biāo)值$y_{i}$和模型給出的預(yù)測(cè)值$\hat{y_{i}}$,損失函數(shù)輸出一個(gè)非負(fù)的實(shí)值。這個(gè)實(shí)值通常用來(lái)反映模型誤差的大小。
對(duì)于線(xiàn)性回歸模型來(lái)講,最常見(jiàn)的損失函數(shù)就是均方誤差(Mean Squared Error, MSE)了,它的形式是:
$$MSE=\frac{1}{n}\sum_{i=1}^{n}{(\hat{Y_i}-Y_i)}^2$$
即對(duì)于一個(gè)大小為$n$的測(cè)試集,$MSE$是$n$個(gè)數(shù)據(jù)預(yù)測(cè)結(jié)果誤差平方的均值。
定義好模型結(jié)構(gòu)之后,我們要通過(guò)以下幾個(gè)步驟進(jìn)行模型訓(xùn)練
1.初始化參數(shù),其中包括權(quán)重$\omega_i$和偏置$b$,對(duì)其進(jìn)行初始化(如0均值,1方差)。
2.網(wǎng)絡(luò)正向傳播計(jì)算網(wǎng)絡(luò)輸出和損失函數(shù)。
3.根據(jù)損失函數(shù)進(jìn)行反向誤差傳播 (backpropagation),將網(wǎng)絡(luò)誤差從輸出層依次向前傳遞, 并更新網(wǎng)絡(luò)中的參數(shù)。
4.重復(fù)2~3步驟,直至網(wǎng)絡(luò)訓(xùn)練誤差達(dá)到規(guī)定的程度或訓(xùn)練輪次達(dá)到設(shè)定值。
首先加載需要的包
import paddle.v2 as paddle
import paddle.v2.dataset.uci_housing as uci_housing
我們通過(guò)uci_housing模塊引入了數(shù)據(jù)集合UCI Housing Data Set
其中,在uci_housing模塊中封裝了:
1.數(shù)據(jù)下載的過(guò)程。下載數(shù)據(jù)保存在~/.cache/paddle/dataset/uci_housing/housing.data。
2.數(shù)據(jù)預(yù)處理的過(guò)程。
這份數(shù)據(jù)集共506行,每行包含了波士頓郊區(qū)的一類(lèi)房屋的相關(guān)信息及該類(lèi)房屋價(jià)格的中位數(shù)。其各維屬性的意義如下:
| 屬性名 | 解釋 | 類(lèi)型 |
|---|---|---|
| CRIM | 該鎮(zhèn)的人均犯罪率 | 連續(xù)值 |
| ZN | 占地面積超過(guò)25,000平方呎的住宅用地比例 | 連續(xù)值 |
| INDUS | 非零售商業(yè)用地比例 | 連續(xù)值 |
| CHAS | 是否鄰近 Charles River | 離散值,1=鄰近;0=不鄰近 |
| NOX | 一氧化氮濃度 | 連續(xù)值 |
| RM | 每棟房屋的平均客房數(shù) | 連續(xù)值 |
| AGE | 1940年之前建成的自用單位比例 | 連續(xù)值 |
| DIS | 到波士頓5個(gè)就業(yè)中心的加權(quán)距離 | 連續(xù)值 |
| RAD | 到徑向公路的可達(dá)性指數(shù) | 連續(xù)值 |
| TAX | 全值財(cái)產(chǎn)稅率 | 連續(xù)值 |
| PTRATIO | 學(xué)生與教師的比例 | 連續(xù)值 |
| B | 1000(BK - 0.63)^2,其中BK為黑人占比 | 連續(xù)值 |
| LSTAT | 低收入人群占比 | 連續(xù)值 |
| MEDV | 同類(lèi)房屋價(jià)格的中位數(shù) | 連續(xù)值 |
觀(guān)察一下數(shù)據(jù),我們的第一個(gè)發(fā)現(xiàn)是:所有的13維屬性中,有12維的連續(xù)值和1維的離散值(CHAS)。離散值雖然也常使用類(lèi)似0、1、2這樣的數(shù)字表示,但是其含義與連續(xù)值是不同的,因?yàn)檫@里的差值沒(méi)有實(shí)際意義。例如,我們用0、1、2來(lái)分別表示紅色、綠色和藍(lán)色的話(huà),我們并不能因此說(shuō)“藍(lán)色和紅色”比“綠色和紅色”的距離更遠(yuǎn)。所以通常對(duì)一個(gè)有$d$個(gè)可能取值的離散屬性,我們會(huì)將它們轉(zhuǎn)為$d$個(gè)取值為0或1的二值屬性或者將每個(gè)可能取值映射為一個(gè)多維向量。不過(guò)就這里而言,因?yàn)镃HAS本身就是一個(gè)二值屬性,就省去了這個(gè)麻煩。
另外一個(gè)稍加觀(guān)察即可發(fā)現(xiàn)的事實(shí)是,各維屬性的取值范圍差別很大(如圖2所示)。例如,屬性B的取值范圍是[0.32, 396.90],而屬性NOX的取值范圍是[0.3850, 0.8170]。這里就要用到一個(gè)常見(jiàn)的操作-歸一化(normalization)了。歸一化的目標(biāo)是把各位屬性的取值范圍放縮到差不多的區(qū)間,例如[-0.5,0.5]。這里我們使用一種很常見(jiàn)的操作方法:減掉均值,然后除以原取值范圍。
做歸一化(或 Feature scaling)至少有以下3個(gè)理由:
1.過(guò)大或過(guò)小的數(shù)值范圍會(huì)導(dǎo)致計(jì)算時(shí)的浮點(diǎn)上溢或下溢。
2.不同的數(shù)值范圍會(huì)導(dǎo)致不同屬性對(duì)模型的重要性不同(至少在訓(xùn)練的初始階段如此),而這個(gè)隱含的假設(shè)常常是不合理的。這會(huì)對(duì)優(yōu)化的過(guò)程造成困難,使訓(xùn)練時(shí)間大大的加長(zhǎng)。
3.很多的機(jī)器學(xué)習(xí)技巧/模型(例如L1,L2正則項(xiàng),向量空間模型-Vector Space Model)都基于這樣的假設(shè):所有的屬性取值都差不多是以0為均值且取值范圍相近的。
http://wiki.jikexueyuan.com/project/deep-learning/images/01-02.png" alt="png" />
圖2. 各維屬性的取值范圍
我們將數(shù)據(jù)集分割為兩份:一份用于調(diào)整模型的參數(shù),即進(jìn)行模型的訓(xùn)練,模型在這份數(shù)據(jù)集上的誤差被稱(chēng)為訓(xùn)練誤差;另外一份被用來(lái)測(cè)試,模型在這份數(shù)據(jù)集上的誤差被稱(chēng)為測(cè)試誤差。我們訓(xùn)練模型的目的是為了通過(guò)從訓(xùn)練數(shù)據(jù)中找到規(guī)律來(lái)預(yù)測(cè)未知的新數(shù)據(jù),所以測(cè)試誤差是更能反映模型表現(xiàn)的指標(biāo)。分割數(shù)據(jù)的比例要考慮到兩個(gè)因素:更多的訓(xùn)練數(shù)據(jù)會(huì)降低參數(shù)估計(jì)的方差,從而得到更可信的模型;而更多的測(cè)試數(shù)據(jù)會(huì)降低測(cè)試誤差的方差,從而得到更可信的測(cè)試誤差。我們這個(gè)例子中設(shè)置的分割比例為$8:2$
在更復(fù)雜的模型訓(xùn)練過(guò)程中,我們往往還會(huì)多使用一種數(shù)據(jù)集:驗(yàn)證集。因?yàn)閺?fù)雜的模型中常常還有一些超參數(shù)(Hyperparameter)需要調(diào)節(jié),所以我們會(huì)嘗試多種超參數(shù)的組合來(lái)分別訓(xùn)練多個(gè)模型,然后對(duì)比它們?cè)隍?yàn)證集上的表現(xiàn)選擇相對(duì)最好的一組超參數(shù),最后才使用這組參數(shù)下訓(xùn)練的模型在測(cè)試集上評(píng)估測(cè)試誤差。由于本章訓(xùn)練的模型比較簡(jiǎn)單,我們暫且忽略掉這個(gè)過(guò)程。
fit_a_line/trainer.py演示了訓(xùn)練的整體過(guò)程。
paddle.init(use_gpu=False, trainer_count=1)
線(xiàn)性回歸的模型其實(shí)就是一個(gè)采用線(xiàn)性激活函數(shù)(linear activation,LinearActivation)的全連接層(fully-connected layer,fc_layer):
x = paddle.layer.data(name='x', type=paddle.data_type.dense_vector(13))
y_predict = paddle.layer.fc(input=x,
size=1,
act=paddle.activation.Linear())
y = paddle.layer.data(name='y', type=paddle.data_type.dense_vector(1))
cost = paddle.layer.mse_cost(input=y_predict, label=y)
parameters = paddle.parameters.create(cost)
optimizer = paddle.optimizer.Momentum(momentum=0)
trainer = paddle.trainer.SGD(cost=cost,
parameters=parameters,
update_equation=optimizer)
PaddlePaddle提供一個(gè) reader機(jī)制 來(lái)讀取數(shù)據(jù)。 Reader返回的數(shù)據(jù)可以包括多列,我們需要一個(gè)Python dict把列 序號(hào)映射到網(wǎng)絡(luò)里的數(shù)據(jù)層。
feeding={'x': 0, 'y': 1}
此外,我們還可以提供一個(gè) event handler,來(lái)打印訓(xùn)練的進(jìn)度:
# event_handler to print training and testing info
def event_handler(event):
if isinstance(event, paddle.event.EndIteration):
if event.batch_id % 100 == 0:
print "Pass %d, Batch %d, Cost %f" % (
event.pass_id, event.batch_id, event.cost)
if isinstance(event, paddle.event.EndPass):
result = trainer.test(
reader=paddle.batch(
uci_housing.test(), batch_size=2),
feeding=feeding)
print "Test %d, Cost %f" % (event.pass_id, result.cost)
# event_handler to print training and testing info
from paddle.v2.plot import Ploter
train_title = "Train cost"
test_title = "Test cost"
cost_ploter = Ploter(train_title, test_title)
step = 0
def event_handler_plot(event):
global step
if isinstance(event, paddle.event.EndIteration):
if step % 10 == 0: # every 10 batches, record a train cost
cost_ploter.append(train_title, step, event.cost)
if step % 100 == 0: # every 100 batches, record a test cost
result = trainer.test(
reader=paddle.batch(
uci_housing.test(), batch_size=2),
feeding=feeding)
cost_ploter.append(test_title, step, result.cost)
if step % 100 == 0: # every 100 batches, update cost plot
cost_ploter.plot()
step += 1
trainer.train(
reader=paddle.batch(
paddle.reader.shuffle(
uci_housing.train(), buf_size=500),
batch_size=2),
feeding=feeding,
event_handler=event_handler_plot,
num_passes=30)
http://wiki.jikexueyuan.com/project/deep-learning/images/01-03.png" alt="png" />
在這章里,我們借助波士頓房?jī)r(jià)這一數(shù)據(jù)集,介紹了線(xiàn)性回歸模型的基本概念,以及如何使用PaddlePaddle實(shí)現(xiàn)訓(xùn)練和測(cè)試的過(guò)程。很多的模型和技巧都是從簡(jiǎn)單的線(xiàn)性回歸模型演化而來(lái),因此弄清楚線(xiàn)性模型的原理和局限非常重要。

本教程 由 PaddlePaddle 創(chuàng)作,采用 知識(shí)共享 署名-相同方式共享 4.0 國(guó)際 許可協(xié)議進(jìn)行許可。