線性回歸讓我們從經(jīng)典的線性回歸(Linear Regression [1])模型開始這份教程,。在這一章里,,你將使用真實的數(shù)據(jù)集建立起一個房價預測模型,,并且了解到機器學習中的若干重要概念,。 本教程源代碼目錄在book/fit_a_line,, 初次使用請參考PaddlePaddle安裝教程,,更多內(nèi)容請參考本教程的視頻課堂,。 背景介紹給定一個大小為的數(shù)據(jù)集 ,其中是第個樣本個屬性上的取值,是該樣本待預測的目標,。線性回歸模型假設(shè)目標可以被屬性間的線性組合描述,,即
例如,在我們將要建模的房價預測問題里,,是描述房子的各種屬性(比如房間的個數(shù),、周圍學校和醫(yī)院的個數(shù)、交通狀況等),,而 是房屋的價格,。 初看起來,這個假設(shè)實在過于簡單了,,變量間的真實關(guān)系很難是線性的,。但由于線性回歸模型有形式簡單和易于建模分析的優(yōu)點,它在實際問題中得到了大量的應(yīng)用,。很多經(jīng)典的統(tǒng)計學習,、機器學習書籍[2,3,4]也選擇對線性模型獨立成章重點講解。 效果展示我們使用從UCI Housing Data Set獲得的波士頓房價數(shù)據(jù)集進行模型的訓練和預測,。下面的散點圖展示了使用模型對部分房屋價格進行的預測,。其中,每個點的橫坐標表示同一類房屋真實價格的中位數(shù),,縱坐標表示線性回歸模型根據(jù)特征預測的結(jié)果,,當二者值完全相等的時候就會落在虛線上。所以模型預測得越準確,,則點離虛線越近,。
模型概覽模型定義在波士頓房價數(shù)據(jù)集中,和房屋相關(guān)的值共有14個:前13個用來描述房屋相關(guān)的各種信息,,即模型中的 ,;最后一個值為我們要預測的該類房屋價格的中位數(shù),即模型中的 ,。因此,,我們的模型就可以表示成:
表示模型的預測結(jié)果,用來和真實值區(qū)分,。模型要學習的參數(shù)即:,。 建立模型后,我們需要給模型一個優(yōu)化目標,,使得學到的參數(shù)能夠讓預測值盡可能地接近真實值,。這里我們引入損失函數(shù)(Loss Function,或Cost Function)這個概念,。 輸入任意一個數(shù)據(jù)樣本的目標值和模型給出的預測值,,損失函數(shù)輸出一個非負的實值,。這個實值通常用來反映模型誤差的大小。 對于線性回歸模型來講,,最常見的損失函數(shù)就是均方誤差(Mean Squared Error,, MSE)了,它的形式是:
即對于一個大小為的測試集,,是個數(shù)據(jù)預測結(jié)果誤差平方的均值,。 訓練過程定義好模型結(jié)構(gòu)之后,我們要通過以下幾個步驟進行模型訓練 1. 初始化參數(shù),,其中包括權(quán)重和偏置,,對其進行初始化(如0均值,1方差),。 2. 網(wǎng)絡(luò)正向傳播計算網(wǎng)絡(luò)輸出和損失函數(shù),。 3. 根據(jù)損失函數(shù)進行反向誤差傳播 (backpropagation),將網(wǎng)絡(luò)誤差從輸出層依次向前傳遞, 并更新網(wǎng)絡(luò)中的參數(shù),。 4. 重復2~3步驟,,直至網(wǎng)絡(luò)訓練誤差達到規(guī)定的程度或訓練輪次達到設(shè)定值。 數(shù)據(jù)集數(shù)據(jù)集接口的封裝首先加載需要的包 import paddle.v2 as paddle import paddle.v2.dataset.uci_housing as uci_housing 我們通過uci_housing模塊引入了數(shù)據(jù)集合UCI Housing Data Set 其中,,在uci_housing模塊中封裝了:
數(shù)據(jù)集介紹這份數(shù)據(jù)集共506行,,每行包含了波士頓郊區(qū)的一類房屋的相關(guān)信息及該類房屋價格的中位數(shù)。其各維屬性的意義如下:
數(shù)據(jù)預處理連續(xù)值與離散值觀察一下數(shù)據(jù),,我們的第一個發(fā)現(xiàn)是:所有的13維屬性中,,有12維的連續(xù)值和1維的離散值(CHAS)。離散值雖然也常使用類似0,、1,、2這樣的數(shù)字表示,但是其含義與連續(xù)值是不同的,,因為這里的差值沒有實際意義。例如,,我們用0,、1、2來分別表示紅色,、綠色和藍色的話,,我們并不能因此說“藍色和紅色”比“綠色和紅色”的距離更遠。所以通常對一個有個可能取值的離散屬性,我們會將它們轉(zhuǎn)為個取值為0或1的二值屬性或者將每個可能取值映射為一個多維向量,。不過就這里而言,,因為CHAS本身就是一個二值屬性,就省去了這個麻煩,。 屬性的歸一化另外一個稍加觀察即可發(fā)現(xiàn)的事實是,,各維屬性的取值范圍差別很大(如圖2所示)。例如,,屬性B的取值范圍是[0.32, 396.90],,而屬性NOX的取值范圍是[0.3850, 0.8170]。這里就要用到一個常見的操作-歸一化(normalization)了,。歸一化的目標是把各位屬性的取值范圍放縮到差不多的區(qū)間,,例如[-0.5,0.5]。這里我們使用一種很常見的操作方法:減掉均值,,然后除以原取值范圍,。 做歸一化(或 Feature scaling)至少有以下3個理由: - 過大或過小的數(shù)值范圍會導致計算時的浮點上溢或下溢。 - 不同的數(shù)值范圍會導致不同屬性對模型的重要性不同(至少在訓練的初始階段如此),,而這個隱含的假設(shè)常常是不合理的,。這會對優(yōu)化的過程造成困難,使訓練時間大大的加長,。 - 很多的機器學習技巧/模型(例如L1,,L2正則項,向量空間模型-Vector Space Model)都基于這樣的假設(shè):所有的屬性取值都差不多是以0為均值且取值范圍相近的,。
整理訓練集與測試集我們將數(shù)據(jù)集分割為兩份:一份用于調(diào)整模型的參數(shù),,即進行模型的訓練,模型在這份數(shù)據(jù)集上的誤差被稱為訓練誤差,;另外一份被用來測試,,模型在這份數(shù)據(jù)集上的誤差被稱為測試誤差。我們訓練模型的目的是為了通過從訓練數(shù)據(jù)中找到規(guī)律來預測未知的新數(shù)據(jù),,所以測試誤差是更能反映模型表現(xiàn)的指標,。分割數(shù)據(jù)的比例要考慮到兩個因素:更多的訓練數(shù)據(jù)會降低參數(shù)估計的方差,從而得到更可信的模型,;而更多的測試數(shù)據(jù)會降低測試誤差的方差,,從而得到更可信的測試誤差。我們這個例子中設(shè)置的分割比例為 在更復雜的模型訓練過程中,,我們往往還會多使用一種數(shù)據(jù)集:驗證集,。因為復雜的模型中常常還有一些超參數(shù)(Hyperparameter)需要調(diào)節(jié),所以我們會嘗試多種超參數(shù)的組合來分別訓練多個模型,,然后對比它們在驗證集上的表現(xiàn)選擇相對最好的一組超參數(shù),,最后才使用這組參數(shù)下訓練的模型在測試集上評估測試誤差,。由于本章訓練的模型比較簡單,我們暫且忽略掉這個過程,。 訓練
初始化PaddlePaddlepaddle.init(use_gpu=False, trainer_count=1) 模型配置線性回歸的模型其實就是一個采用線性激活函數(shù)(linear activation, 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.square_error_cost(input=y_predict, label=y) 保存網(wǎng)絡(luò)拓撲# Save the inference topology to protobuf. inference_topology = paddle.topology.Topology(layers=y_predict) with open("inference_topology.pkl", 'wb') as f: inference_topology.serialize_for_inference(f) 創(chuàng)建參數(shù)parameters = paddle.parameters.create(cost) 創(chuàng)建Traineroptimizer = paddle.optimizer.Momentum(momentum=0) trainer = paddle.trainer.SGD(cost=cost, parameters=parameters, update_equation=optimizer) 讀取數(shù)據(jù)且打印訓練的中間信息PaddlePaddle提供一個 reader機制 來讀取數(shù)據(jù),。 Reader返回的數(shù)據(jù)可以包括多列,我們需要一個Python dict把列 序號映射到網(wǎng)絡(luò)里的數(shù)據(jù)層,。 feeding={'x': 0, 'y': 1} 此外,,我們還可以提供一個 event handler,來打印訓練的進度: # 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 if isinstance(event, paddle.event.EndPass): if event.pass_id % 10 == 0: with open('params_pass_%d.tar' % event.pass_id, 'w') as f: trainer.save_parameter_to_tar(f) 開始訓練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) 應(yīng)用模型1. 生成測試數(shù)據(jù)test_data_creator = paddle.dataset.uci_housing.test() test_data = [] test_label = [] for item in test_data_creator(): test_data.append((item[0],)) test_label.append(item[1]) if len(test_data) == 5: break 2. 推測 inference# load parameters from tar file. # users can remove the comments and change the model name # with open('params_pass_20.tar', 'r') as f: # parameters = paddle.parameters.Parameters.from_tar(f) probs = paddle.infer( output_layer=y_predict, parameters=parameters, input=test_data) for i in xrange(len(probs)): print "label=" + str(test_label[i][0]) + ", predict=" + str(probs[i][0]) 總結(jié)在這章里,,我們借助波士頓房價這一數(shù)據(jù)集,,介紹了線性回歸模型的基本概念,以及如何使用PaddlePaddle實現(xiàn)訓練和測試的過程,。很多的模型和技巧都是從簡單的線性回歸模型演化而來,,因此弄清楚線性模型的原理和局限非常重要。 參考文獻
,。 |
|