1. 程式人生 > >機器學習-TensorFlow建模過程 Linear Regression線性擬合應用

機器學習-TensorFlow建模過程 Linear Regression線性擬合應用

TensorFlow是咱們機器學習領域非常常用的一個元件,它在資料處理,模型建立,模型驗證等等關於機器學習方面的領域都有很好的表現,前面的一節我已經簡單介紹了一下TensorFlow裡面基礎的資料結構即:Tensor和Dataset; 這裡咱們開始介紹TensorFlow的建模過程以及驗證模型的一些簡單方法。其實無論是sklearn還是TensorFlow,他們的模型建立過程都是相似的,都是經歷columns型別宣告,模型定義,資料訓練,validation等等幾個步驟。前面的幾節內容我已經簡單的介紹瞭如何用sklearn建立tree_based模型,這裡我主要是想演示TensorFlow的應用,所以我就用linear regressor來當做例子來演示TensorFlow是如何從資料載入一直到資料驗證的階段。至於線性擬合的實現的具體細節,我在下一節的內容會從數學的角度具體解釋的。本節內容所使用的資料都是來自於網路中,大家可以忽略具體的資料的意思,主要的瞭解TensorFlow的應用過程,不必過於糾結於模型的細節部分,模型的細節我會在隨後的章節解釋。好了,那麼咱們現在開始吧

第一步:資料準備

顧名思義,就是咱們準備資料的過程,這裡包括有missing value handling, categorical data encoding,data split, data permutation等等內容,這一步咱們要將咱們將來模型訓練所用到的資料都能準備好。這個準備過程無非也就是上面的這些步驟,咱們可以看下面的程式碼演示

cali_housing_dataset_original = pd.read_csv("https://download.mlcc.google.com/mledu-datasets/california_housing_train.csv")
cali_housing_dataset_original["median_house_value"] /= 1000.0
#create a random generator
generator = np.random.Generator(np.random.PCG64())
#permutate the data frame
cali_housing_dataset_permutation = cali_housing_dataset_original.reindex(
        generator.permutation(cali_housing_dataset_original.index)
        )
cali_housing_dataset_permutation.describe()

#select the features that we will use in the model trainning process
my_feature = cali_housing_dataset_permutation[["total_rooms"]]
#select the targets of the dataset
targets = cali_housing_dataset_permutation[["median_house_value"]]

這裡我就不演示那些feature engineering的過程,那些內容太多,大家可以看我之前的部落格,這裡主要是想向大家演示一下permutation的過程。因為最新的numpy對於randomize的過程已經有更新了,不再使用的那些老的API了,numpy中最新的randomize的過程是通過建立2個generator來實現隨機過程和隨機數的生成了,這兩個generator一個個是bit generator, 就如咱們上面程式碼中的PCG64(), 它能產生一個隨機的bit stream, 根據numpy的官方文件,雖然有很多種bit generator,但是PCG64是最穩定的一種;另外一個就是Generator, 它是通過np.random.Generator()函式來例項化一個物件的,它能將bit generator產生的bit stream轉化成數字。這裡的資料咱們就選擇一個最最簡單的linear regression的方式,那就是隻選擇一個feature,那就是total_rooms; 咱們的target是median_house_value。

第二步:定義feature 型別 和 optimizer

 既然咱們的資料都準備好了,那麼之後那麼得定義一下咱們資料的型別(每一個column的型別都得定義),將來在咱們定義模型的時候咱們也得將columns的資訊傳遞給咱們的模型物件;以及用什麼optimizer將來來訓練咱們的模型,這個optimizer將來也得傳遞給咱們的模型物件。 具體optimizer是什麼我下面會慢慢講的。為了方便演示,還是先上程式碼給大家看

#indicates what is the data type of the feature to tensorflow
feature_columns = [tf.feature_column.numeric_column("total_rooms")]
#using stochastic gradient descent as the optimizer for our model #to ensure the magtitute of gradient do not become too large, we apply clipping_norm to our optimizer my_optimizer = tf.optimizers.SGD(learning_rate = 0.0000001, clipnorm=5.0)

從上面的程式碼可以看出,第一句是宣告咱們feature_columns裡面只有一個numeric_column,記住每一個column都有一個feature_column物件,這裡因為咱們只選取了一個feature是total_rooms,所以咱們這裡就一個tf.feature_column物件。這裡的第二行程式碼是咱們的重點,也是以後優化模型中經常要調整的部分,這裡咱們可以看出,這裡的optimizer是一個SGD, SGD是stochastic gradient descent的縮寫,就是每一次計算咱們的gradient descent的時候,咱們只選取一組資料進行計算,如果每一次計算gradient descent的時候咱們都用整個資料進行計算,那麼咱們的計算機是負擔不起的,消耗的儲存空間和計算能力都太大了,因為在實際中咱們的資料集的數量經常都是以萬為單位的。具體計算gradient descent的過程我會在下一節中講述模型訓練過程中演示的。咱們可以看出來,SGD中還有兩個引數,分別是learning_rate和clipnorm, 咱們知道,當我們在訓練我們的模型的時候,我們需要逐步的訓練很多次,知道咱們的gradient descent趨於0是才停止,咱們的每一步的大小都要合理,如果learning_rate太小,咱們訓練的步數就會太多,影響咱們的效率;如果learning_rate太大,則可能導致咱們訓練模型的過程不能converge,就是永遠找不到那個最小值,從而導致訓練的模型失敗;為了防止咱們咱們的gradient太大,我們這裡不單單用learning_rate來限制,咱們還加了一個clipnorm來限制咱們的gradient magtitute大小,防止咱們fail to converge, 這相當於一個雙重保險。

第三步:定義一個模型model

將上面的引數都定義完成後,咱們就得定義一下咱們的模型啦,TensorFlow提供了大量的模型可供使用,幾乎所有主流的機器學習的模型和深度學習相關的模型,TensorFlow幾乎實現全覆蓋了,具體咱們可以去他的官網查詢, 他的官網地址是:https://www.tensorflow.org/api_docs/python/tf  ,記住在TensorFlow中,他的模型都在tf.estimator這個模組中。因為這裡是咱們講述用TensorFlow開發機器學習應用的入門,咱們就選一個最簡單的模型linear regressor模型來演示

linear_regressor = tf.estimator.LinearRegressor(
        feature_columns = feature_columns,
        optimizer = my_optimizer
        )

這裡咱們可以看出是如何初始化一個LinearRegressor物件的,同樣的,咱們可以看出來它初始化的時候也是需要傳遞feature_columns和optimizer這2個引數的,而這兩個引數正是咱們第二步中所初始化的,可以說是環環相扣啊,哈哈,也可以看出咱們前面定義初始化的一個物件都是沒有多餘的,都是要用到的。這兩個引數分別告訴了咱們的模型咱們資料columns的型別以及用什麼optimizer來訓練這2個資訊。

第四步:資料來源input_fn

既然咱們的原始資料準備好了,模型也都定義好了,如果需要訓練咱們的模型,咱們還差什麼呢?對了就是將咱們的原始資料(這裡的例子是dataframe)轉化成TensorFlow中的dataset,並將轉化後的data傳遞給咱們的模型,讓咱們之前定義的模型用這些資料去訓練。這裡應該也是咱們用TensorFlow來建模的一個核心部分了,咱們先看程式碼演示,然後我會逐個詳細解釋的

def my_input(features, targets, batch_size=500, shuffle=True, num_epochs=None):
    
    """
    epochs: while trainning, in the case of steps is larger than the length of dataset, we set the epochs to None, which means repeat forever. However,
    in trainning process, we have the steps to control the total number of iterations.  While in the case of making predictions of a given dataset, we must
    set epochs to 1 and shuffle to False. Because we only want the input function return the dataset once, otherwise the function will keep returnning the 
    results forvere once and once again.
    
    shuffle: in the trainning process, in order to balance the dataset, we set it to True. However while in prediction process, we have to set it to False, which
    could help us to evaluate the outputs. For example, if the outputs are shuffled, we have no way to compare the outputs to our original lables.
    
    """
    
    #convert panda dataframe to a dict of Numpy array
    features = {key:tf.multiply(np.array(value),1) for key,value in dict(features).items()}
    #construct a dataset
    ds = tf.data.Dataset.from_tensor_slices((features,targets))
    ds = ds.batch(batch_size).repeat(num_epochs)
    
    if shuffle:
        ds = ds.shuffle(buffer_size = 10000)    
    return ds

這裡有幾個核心的引數我需要解釋一下,首先features和target兩個引數很明顯就是咱們第一步中獲取的資料,分別是用來訓練這個模型的特徵變數和label;重點這裡解釋一下batch_size, shuffle 和 num_epochs這三個引數,這三個引數在咱們TensorFlow的整個學習過程中都得用到,絕對的重點,不容懷疑。首先咱們來看batch_size, 因為SGD每一次都只會選用一條資料來計算咱們的gradient(訓練模型),而咱們的資料具有很強的隨機性,那麼就會導致咱們的模型最後很可能不可用,但是如果咱們每一個step都選用整個dataset來訓練模型,又會導致咱們的訓練過程非常漫長,那麼聰明的人類就自然而然的想到了咱們可以每一次選用一定數量的資料來訓練模型,一般的資料量咱們大致的範圍都是在10-10000之間,這種方式就成為mini-batch SGD, 在這裡咱們就是採用了這種方式,咱們每一次選用500條資料來訓練咱們的模型,這是通過設定batch_size的值來實現的。對於shuffle這個引數呢,也是為了打亂咱們的資料來進行訓練,最終的目的也是為了能幫助咱們訓練出更加精確的模型,防止咱們的資料分佈不合理導致模型有偏差,它裡面的buffer_size的意思是先從ds中選中buffer_size個數據(這些資料暫時還是有序的,順序和ds中一樣),然後iterate的時候呢,就從這個buffer中隨機的選擇資料(這個選擇資料的過程就是無序的選擇了,實現了隨機的目的)。最後還有這個repeat(num_epochs)的函式,首先repeat函式在training的過程中一定需要的,因為當咱們設定steps步數引數的時候,如果steps的總數要多餘整個dataset的資料量的時候,那麼這時候咱們一定得重複利用咱們的dataset來達到訓練的目的,否則咱們的資料來源的資料量不夠了,會出錯的,這就是為什麼需要repeat的原因,至於num_epochs是來控制重複的次數的,一般在training階段咱們將它設定成None, 意思就是無限迴圈,知道training中的steps全部走完位置,如果在predict階段,咱們一般就設定成1,因為咱們驗證階段不需要重複的,同樣的在predict的時候,資料來源函式中的shuffle也要設定成False的,否則predict的結果都是亂序的,無法跟原來的資料進行對比了。前面幾個引數在咱們模型訓練過程中可以說是最重要的引數了,這裡說的也比較多,所以一點得用心搞明白。

第五步:訓練模型 training

既然上面咱們把模型的引數都設定好了,資料來源也定義好了,那麼接下來咱們的任務就是訓練咱們定義的模型了,這個過程在程式碼中是很簡單的,但它內部做的工作是很多的,它需要計算在每個維度(feature)上的gradient descent,知道它趨於0為止,它的計算細節我會在接下來的一個章節中展示出來,咱們先看程式碼

linear_regressor.train(
        input_fn = lambda:my_input(my_feature, targets),
        steps = 1000
        )

是不是很簡單,咱們只需要將資料來源函式作為引數傳遞給他,並且設定一個steps就可以了。這裡input_fn我就不解釋了,簡單說一下steps,steps指的是咱們訓練的步數,咱們每計算一次gradient descent,就算一步,這裡指咱們最多計算1000次,即使1000的時候gradient descent不等於0咱也停止咱的訓練過程,然後咱們可以重新設定optimizer中的learning_rate來重新訓練。

第六步:predict和evaluate

經過前面五步後,咱們已經訓練出來了一個模型,那麼接下來咱們需要用這個模型來預測一下資料,並且看看它的效果,去evaluate一下這個模型。正常的情況下咱們會將資料split成training data 和 validation data,這裡我為了方便,咱就直接用training data來演示如何predict還有如何evaluate咱們的模型。簡單的程式碼如下

#create a input function for prediction
prediction_input_fn = lambda:my_input(my_feature,targets,shuffle=False,num_epochs=1)
#prediction
predictions = linear_regressor.predict(input_fn = prediction_input_fn)
predictions = np.array([item["predictions"][0] for item in predictions])

#errors MSE
mean_squared_error(targets,predictions)

在咱們做prediction的時候,咱們也是需要資料來源input_fn的,在prediction的時候,shuffle=False, num_epochs=1; 然後呼叫這個模型linear_regressor的predict方法,將資料來源函式傳遞給他, 它返回的結果是一個list,這個list裡面的element是一個dictionary,這個dictionary的key值“predictions”, value也是一個一個list,並且只有一個元素element,此element就是咱們要的結果。最後咱們要evaluate這個模型預測的結果,咱們有很多種方式可以驗證,這裡只展示了一個最簡單的方式,就是計算咱們的target和prediction的方差,其實有很多很多種方式,在後面的章節我會慢慢介紹。

第七步:data visualization

好了,最後咱們來看一下根據咱們的學習的模型,咱們想看看它的具體的擬合的效果,這裡就需要用一點之前咱們學習的資料視覺化的內容了,這裡比較簡單,咱們通過模型學習到的引數,畫一條擬合線,然後在將資料畫到畫布上,座標分別是"total_rooms"和"house_median_price",然後通過scatter plot展示出來。程式碼如下

sample = cali_housing_dataset_permutation.sample(n=300)
x_0 = sample["total_rooms"].min()
x_1 = sample["total_rooms"].max()
linear_regressor.get_variable_names()#retrieve the name of the variable
weights = linear_regressor.get_variable_value("linear/linear_model/total_rooms/weights")[0]#returns the value of variable given by name
bias = linear_regressor.get_variable_value("linear/linear_model/bias_weights")#retruns the value of bias
y_0 = weights*x_0+bias
y_1 = weights*x_1+bias
plt.plot([x_0,x_1],[y_0,y_1])#plot our regression line
plt.ylabel("median_house_value")#label the y Axes
plt.xlabel("total_rooms")#label the x Axes
plt.scatter(sample["total_rooms"],sample["median_house_value"])#plot a scatter plot from the sample
plt.show()

結果如下

 

 可以看得出來,擬合的還不錯。嘿嘿,關於模型訓練過程的視覺化,後面還有很多種,以後我慢慢說,例如:x座標是steps, y座標是loss, 也是非常常見的一種方式。

總結

今天完整的展示了用TensorFlow建立模型的整個過程,一直從data preparation到最後的evaluation,可以說貫穿了TensorFlow開發機器學習應用的整個過程。今天先用一個最簡單的線性擬合例子展示這個過程,後面我還會展示更多的更加複雜的模型,例如:Logistic Regression, DNN, LSTM,等等等等。但是萬變不離其宗,他們的基礎步驟都是上面的七個步驟。最後 ,祝武漢加油!!!!!!