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,等等等等。但是萬變不離其宗,他們的基礎步驟都是上面的七個步驟。