动手学PaddlePaddle(2):房价预测
通過這個練習可以了解到:
機器學習的典型過程:
-
獲取數據
-
數據預處理
-訓練模型
-應用模型
fluid訓練模型的基本步驟:
-
配置網絡結構:
-
定義成本函數avg_cost
-
定義優化器optimizer
-
獲取訓練數據
-
定義運算場所(place)和執行器(exe)
-
提供數據(feeder)
-
執行訓練(exe.run)
-
預測infer()并輸出擬合圖像
目錄
1 - 引用庫
2 - 數據預處理
3 - 定義reader
4 - 訓練過程
5 - 預測
1 - 引用庫
首先載入需要用到的庫,它們分別是:
- numpy:NumPy是Python語言的一個擴展程序庫。支持高端大量的維度數組與矩陣運算,此外也針對數組運算提供大量的數學函數庫。NumPy的核心功能是"ndarray"(即n-dimensional array,多維數組)數據結構。
- matplotlib.pyplot:用于生成圖,在驗證模型準確率和展示成本變化趨勢時會使用到
- paddle.fluid:引入PaddlePaddle深度學習框架的fluid版本庫;
- pandas:Pandas是python第三方庫,提供高性能易用數據類型和分析工具,Pandas基于Numpy實現,常與Numpy和Matplotlib一同使用
2 - 數據預處理
本次數據集使用的是2016年12月份某市某地區的房價分布。為了簡化模型,假設影響房價的因素只有房屋面積,因此數據集只有兩列,以txt的形式儲存。
當真實數據被收集到后,它們往往不能直接使用。
例如本次數據集使用了某地區的房價分布,為了簡化模型數據只有兩維,并沒有標出來每一列代表什么,其實分別是房屋面積與房屋價格。可以看到房價與房屋面積之間存在一種關系,這種關系究竟是什么,就是本次預測想要得到的結論。可以首先以表格的形式輸出數據的前五行看一下。
一般拿到一組數據后,第一個要處理的是數據類型不同的問題。如果各維屬性中有離散值和連續值,就必須對離散值進行處理。
離散值雖然也常使用類似0、1、2這樣的數字表示,但是其含義與連續值是不同的,因為這里的差值沒有實際意義。例如,用0、1、2來分別表示紅色、綠色和藍色的話,并不能因此說“藍色和紅色”比“綠色和紅色”的距離更遠。通常對有d個可能取值的離散屬性,會將它們轉為d個取值為0或1的二值屬性或者將每個可能取值映射為一個多維向量。
不過就這里而言,數據中沒有離散值,就不用考慮這個問題了。
** 歸一化 **
觀察一下數據的分布特征,一般而言,如果樣本有多個屬性,那么各維屬性的取值范圍差異會很大,這就要用到一個常見的操作-歸一化(normalization)了。歸一化的目標是把各維屬性的取值范圍放縮到差不多的區間,例如[-0.5, 0.5]。這里使用一種很常見的操作方法:減掉均值,然后除以原取值范圍。
# coding = utf-8 # global x_raw,train_data,test_data data = np.loadtxt('./datasets/data.txt',delimiter = ',') x_raw = data.T[0].copy() #axis=0,表示按列計算 #data.shape[0]表示data中一共有多少行 maximums, minimums, avgs = data.max(axis=0), data.min(axis=0), data.sum(axis=0)/data.shape[0] print(maximums) print(minimums) print(avgs) print("the raw area :",data[:,0].max(axis = 0))#歸一化,data[:,i]表示第i列的元素for i in range(data.shape[0]):data[i,0] = (data[i,0] - avgs[0]) / (maximums[0] - minimums[0])#data[i,0] = (data[i,0] - minimums[0]) / (maximums[0] - minimums[0])print('normalization:',data[:,0].max(axis = 0))基本上所有的數據在拿到后都必須進行歸一化,至少有以下3條原因:
1.過大或過小的數值范圍會導致計算時的浮點上溢或下溢。
2.不同的數值范圍會導致不同屬性對模型的重要性不同(至少在訓練的初始階段如此),而這個隱含的假設常常是不合理的。這會對優化的過程造成困難,使訓練時間大大加長。
3.很多的機器學習技巧/模型(例如L1,L2正則項,向量空間模型-Vector Space Model)都基于這樣的假設:所有的屬性取值都差不多是以0為均值且取值范圍相近的。
** 數據集分割 **
將原始數據處理為可用數據后,為了評估模型的好壞,我們將數據分成兩份:訓練集和測試集。
- 訓練集數據用于調整模型的參數,即進行模型的訓練,模型在這份數據集上的誤差被稱為訓練誤差;
- 測試集數據被用來測試,模型在這份數據集上的誤差被稱為測試誤差。
訓練模型的目的是為了通過從訓練數據中找到規律來預測未知的新數據,所以測試誤差是更能反映模型表現的指標。分割數據的比例要考慮到兩個因素:更多的訓練數據會降低參數估計的方差,從而得到更可信的模型;而更多的測試數據會降低測試誤差的方差,從而得到更可信的測試誤差。這個例子中設置的分割比例為8:2。
ratio = 0.8 offset = int(data.shape[0]*ratio)train_data = data[:offset] test_data = data[offset:]print(len(data)) print(len(train_data))3 - 定義reader
構造read_data()函數,來讀取訓練數據集train_set或者測試數據集test_set。它的具體實現是在read_data()函數內部構造一個reader(),使用yield關鍵字來讓reader()成為一個Generator(生成器),注意,yield關鍵字的作用和使用方法類似return關鍵字,不同之處在于yield關鍵字可以構造生成器(Generator)。雖然我們可以直接創建一個包含所有數據的列表,但是由于內存限制,我們不可能創建一個無限大的或者巨大的列表,并且很多時候在創建了一個百萬數量級別的列表之后,卻只需要用到開頭的幾個或幾十個數據,這樣造成了極大的浪費,而生成器的工作方式是在每次循環時計算下一個值,不斷推算出后續的元素,不會創建完整的數據集列表,從而節約了內存使用。
def read_data(data_set):"""一個readerArgs:data_set -- 要獲取的數據集Return:reader -- 用于獲取訓練集及其標簽的生成器generator"""def reader():"""一個readerArgs:Return:data[:-1],data[-1:] --使用yield返回生成器data[:-1]表示前n-1個元素,也就是訓練數據,data[-1:]表示最后一個元素,也就是對應的標簽"""for data in data_set:yield data[:-1],data[-1:]return reader#測試readertest_array = ([10,100],[20,200]) print("test_array for read_data:") for value in read_data(test_array)():print(value)接下來我們定義了用于訓練的數據提供器。提供器每次讀入一個大小為BATCH_SIZE的數據批次。如果用戶希望加一些隨機性,它可以同時定義一個批次大小和一個緩存大小。這樣的話,每次數據提供器會從緩存中隨機讀取批次大小那么多的數據。我們都可以通過batch_size進行設置,這個大小一般是2的N次方。
關于參數的解釋如下:
- paddle.reader.shuffle(read_data(train_data), buf_size=500)表示從read_data(train_data)中讀取了buf_size=500大小的數據并打亂順序
- paddle.batch(reader(), batch_size=BATCH_SIZE)表示從打亂的數據中再取出BATCH_SIZE=20大小的數據進行一次迭代訓練
如果buf_size設置的數值大于數據集本身,就直接把整個數據集打亂順序;如果buf_size設置的數值小于數據集本身,就按照buf_size的大小打亂順序。
BATCH_SIZE = 8# 設置訓練reader train_reader = paddle.batch(paddle.reader.shuffle(read_data(train_data), buf_size=500),batch_size=BATCH_SIZE)#設置測試 reader test_reader = paddle.batch(paddle.reader.shuffle(read_data(test_data), buf_size=500),batch_size=BATCH_SIZE)4 - 訓練過程
完成了數據的預處理工作并構造了read_data()來讀取數據,接下來將進入模型的訓練過程,使用PaddlePaddle來定義構造可訓練的線性回歸模型,關鍵步驟如下:
-
配置網絡結構和設置參數
- 配置網絡結構
- 定義損失函數cost
- 定義執行器(參數隨機初始化)
- 定義優化器optimizer
-
模型訓練
-
預測
-
繪制擬合圖像
定義運算場所:
首先進行最基本的運算場所定義,在 fluid 中使用 place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace() 來進行初始化:
- place 表示fluid program的執行設備,常見的有 fluid.CUDAPlace(0) 和 fluid.CPUPlace()
- use_cuda = False 表示不使用 GPU 進行加速訓練
配置網絡結構和設置參數:
# 輸入層,fluid.layers.data表示數據層,name=’x’:名稱為x,輸出類型為tensor # shape=[1]:數據為1維向量 # dtype='float32':數據類型為float32x = fluid.layers.data(name='x', shape=[1], dtype='float32')# 標簽數據,fluid.layers.data表示數據層,name=’y’:名稱為y,輸出類型為tensor # shape=[1]:數據為1維向量 y = fluid.layers.data(name='y', shape=[1], dtype = 'float32')# 輸出層,fluid.layers.fc表示全連接層,input=x: 該層輸入數據為x # size=1:神經元個數,act=None:激活函數為線性函數 y_predict = fluid.layers.fc(input=x, size=1, act=None)定義損失函數:
# 定義損失函數為均方差損失函數,并且求平均損失,返回值名稱為avg_loss avg_loss = fluid.layers.square_error_cost(input = y_predict, label = y) avg_loss = fluid.layers.mean(avg_loss)定義執行器(參數隨機初始化):
exe = fluid.Executor(place)配置訓練程序:
main_program = fluid.default_main_program() # 獲取默認/全局主函數 startup_program = fluid.default_startup_program() # 獲取默認/全局啟動程序#克隆main_program得到test_program #有些operator在訓練和測試之間的操作是不同的,例如batch_norm,使用參數for_test來區分該程序是用來訓練還是用來測試 #該api不會刪除任何操作符,請在backward和optimization之前使用 test_program = main_program.clone(for_test=True)優化方法:
# 創建optimizer,更多優化算子可以參考 fluid.optimizer() learning_rate = 0.0005 sgd_optimizer = fluid.optimizer.SGD(learning_rate) sgd_optimizer.minimize(avg_loss) print("optimizer is ready")創建訓練過程:
訓練需要有一個訓練程序和一些必要參數,并構建了一個獲取訓練過程中測試誤差的函數。必要參數有executor,program,reader,feeder,fetch_list,executor表示之前創建的執行器,program表示執行器所執行的program,是之前創建的program,如果該項參數沒有給定的話則默認使用defalut_main_program,reader表示讀取到的數據,feeder表示前向輸入的變量,fetch_list表示用戶想得到的變量或者命名的結果。
# For training test cost def train_test(executor, program, reader, feeder, fetch_list):accumulated = 1 * [0]count = 0for data_test in reader():outs = executor.run(program=program, feed=feeder.feed(data_test), fetch_list=fetch_list)accumulated = [x_c[0] + x_c[1][0] for x_c in zip(accumulated, outs)] # 累加測試過程中的損失值count += 1 # 累加測試集中的樣本數量return [x_d / count for x_d in accumulated] # 計算平均損失#定義模型保存路徑: #params_dirname用于定義模型保存路徑。 params_dirname = "easy_fit_a_line.inference.model"訓練主循環:
#用于畫圖展示訓練cost from paddle.utils.plot import Ploter train_prompt = "Train cost" test_prompt = "Test cost" plot_prompt = Ploter(train_prompt, test_prompt) step = 0# 訓練主循環 feeder = fluid.DataFeeder(place=place, feed_list=[x, y]) exe.run(startup_program)exe_test = fluid.Executor(place)#num_epochs=100表示迭代訓練100次后停止訓練。 num_epochs = 150for pass_id in range(num_epochs):for data_train in train_reader():avg_loss_value, = exe.run(main_program,feed=feeder.feed(data_train),fetch_list=[avg_loss])if step % 10 == 0: # 每10個批次記錄并輸出一下訓練損失plot_prompt.append(train_prompt, step, avg_loss_value[0])plot_prompt.plot()#print("%s, Step %d, Cost %f" %(train_prompt, step, avg_loss_value[0]))if step % 100 == 0: # 每100批次記錄并輸出一下測試損失test_metics = train_test(executor=exe_test,program=test_program,reader=test_reader,fetch_list=[avg_loss.name],feeder=feeder)plot_prompt.append(test_prompt, step, test_metics[0])plot_prompt.plot()#print("%s, Step %d, Cost %f" %(test_prompt, step, test_metics[0]))if test_metics[0] < 10.0: # 如果準確率達到要求,則停止訓練breakstep += 1if math.isnan(float(avg_loss_value[0])):sys.exit("got NaN loss, training failed.")#保存訓練參數到之前給定的路徑中 if params_dirname is not None:fluid.io.save_inference_model(params_dirname, ['x'], [y_predict], exe)5 - 預測
通過fluid.io.load_inference_model,預測器會從params_dirname中讀取已經訓練好的模型,來對從未遇見過的數據進行預測。
print(test_metics) infer_exe = fluid.Executor(place) inference_scope = fluid.core.Scope()預測
預測器會從params_dirname中讀取已經訓練好的模型,來對從未遇見過的數據進行預測。
- tensor_x:生成batch_size個[0,1]區間的隨機數,以 tensor 的格式儲存
- results:預測對應 tensor_x 面積的房價結果
- raw_x:由于數據處理時我們做了歸一化操作,為了更直觀的判斷預測是否準確,將數據進行反歸一化,得到隨機數對應的原始數據。
?
**(5)繪制擬合圖像 **
通過訓練,本次線性回歸模型輸出了一條擬合的直線,想要直觀的判斷模型好壞可將擬合直線與數據的圖像繪制出來。
import numpy as np import matplotlib.pyplot as pltdef plot_data(data):x = data[:,0]y = data[:,1]y_predict = x*a + bplt.scatter(x,y,marker='.',c='r',label='True')plt.title('House Price Distributions')plt.xlabel('House Area ')plt.ylabel('House Price ')plt.xlim(0,250)plt.ylim(0,2500)predict = plt.plot(x,y_predict,label='Predict')plt.legend(loc='upper left')plt.savefig('result1.png')plt.show()data = np.loadtxt('./datasets/data.txt',delimiter = ',') plot_data(data)?
總結
以上是生活随笔為你收集整理的动手学PaddlePaddle(2):房价预测的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: popupkiller.exe是什么进程
- 下一篇: 5, Data Augmentation