1. 程式人生 > >機器學習-淺談神經網路和Keras的應用

機器學習-淺談神經網路和Keras的應用

  • 概述

  神經網路是深度學習的基礎,它在人工智慧中有著非常廣泛的應用,它既可以應用於咱們前面的章節所說的Linear Regression, classification等問題,它還廣泛的應用於image recognition,NLP 等等應用中,當然啦,這一節咱們主要講述神經網路的最基礎的結構以及應用,在後面我會逐漸的講解基於咱們的這個最簡單的神經網路結構的一些其他方面的優化和提升,例如有RNN,CNN等等。這一節主要講解一下咱們的神經網路的結構,以及如何用TensorFlow和Keras構建一個神經網路,以及常用的一些儲存,載入網路模型的一些方式。

  • 神經網路

  神經網路咱們已經聽過很多次了,可是它具體長什麼樣,它的結構是什麼樣子呢?只要大家看懂了下面的圖,大家就能理解最基本的神經網路的結構了,我會結合著下面的圖來解釋DNN的一些基本概念

上面的DNN的圖片是我自己畫的,它是一個最基本的DNN的結構;一個神經網路其實主要包括三個部分,分別是Input layer, hidden layers 和 output layer。input layer就是相當於咱們的資料輸入,input layer中每一個node都是一個feature,如果咱們的dataset有5個feature,那麼咱們的input layer就有5個node;最後一個output layer相當於咱們的target,output layer的node也有可能是多個的不一定只有一個node哦,例如如果咱們的target是class, 假設一共有10中classes的可能,那麼這裡的target就是一個one-hot encoding的資料,每一個target都有10個元素,那麼這時候咱們output layer的node就是10個了。Hidden layers則是咱們使用者定義的layer了,要根據具體的問題具體的分析,如果咱們的問題很複雜,則hidden layer就越多,咱們運算的速度也就越慢,反之亦然;如果細心的朋友肯定會注意到咱的DNN圖片還有另外一種紅色的layer,那就是activation layer,這是什麼呢??這是因為在咱們的DNN如果沒有activation layer,那麼咱們可以想象的出,咱們的模型無論是多麼的複雜,咱最終的模型都是線性的,這時候咱們的模型只適合於linear regression的情況;對於想classification的問題,咱們必須要加一些非線性的函式來讓咱們的DNN模型最終能夠用於non-linear的情況,activation layer就是這些非線性的函式,這裡主要用到的有sigmoid, softmax和relu。所以在linear的情況時候,咱們是不需要activation layer的,在non-linear的問題中,咱們則必須要要用activation layer。另外,DNN圖片中中的weight咱們都是用箭頭表示的,咱們在訓練一個DNN的時候,其實也就是的不多的訓練這些weight,通過gradient descent的方式最終找出最合理的weights,這些weights的初始值有很多種方式來設定,既可以都設定成零,也可以按照一定的規則設定成隨機數,在tf.keras中有很多種方式來設定初始值的。上面就是一個最簡單的DNN的結構,以及這個結構的一些基本的概念,至於咱們是如何來訓練這個模型的,通過什麼方式來求這個DNN的gradient descent的,這中間其實涉及到了DNN 的back propagation的,具體細節我會在後面的章節細講的。這裡大家主要理解一個forward propagation的DNN的結構和過程,以及他的應用就行了。下面我就講述一下如何用TensorFlow和Keras來應用實現上面的DNN。

 

  • TensorFlow應用之實現DNN

   這裡咱們講述一下如何用TensorFlow來定義咱們的DNN,並且訓練DNN模型。其實在TensorFlow中,訓練DNN的過程跟我前面隨筆中寫的linear regression的流程是一模一樣的,從資料準備一種的最後的模型的evaluation都是一樣的,只是在模型的定義中有一點點細微的區別,我在這裡把整個流程的程式碼都貼出來,然後分析一下他跟其他模型訓練的一些不同點

import pandas as pd
import numpy as np
import tensorflow as tf
from sklearn import metrics
import math
"Step1: Data preparation"
#data loading
cali_housing_price_origin = pd.read_csv("https://download.mlcc.google.com/mledu-datasets/california_housing_train.csv")
generator = np.random.Generator(np.random.PCG64())
cali_housing_price_permutation = cali_housing_price_origin.reindex(generator.permutation(cali_housing_price_origin.index))
#preprocess features
def preprocess_data(data_frame):
    feature_names = ["longitude",
     "housing_median_age",
     "total_rooms",
     "total_bedrooms",
     "population",
     "households",
     "median_income"]
    data_frame=data_frame.copy()
    features = data_frame[feature_names]
    features["rooms_per_person"]=features["total_rooms"]/features["population"]
    return features
#preprocess targets
def preprocess_targets(data_frame):
    target = pd.DataFrame()
    target["median_house_value"] = data_frame["median_house_value"]/1000.0
    return target
features = preprocess_data(cali_housing_price_permutation)
target = preprocess_targets(cali_housing_price_permutation)
#trainning
features_trainning = features.head(12000)
target_trainning = target.head(12000)
#validation
features_validation = features.tail(5000)
target_validation = target.tail(5000)

"Step2: Building a neuro network"
#construct feature columns
def construct_feature_columns(features):
    return [tf.feature_column.numeric_column(my_feature) for my_feature in features]
#construct input function
def input_func(features,target,shuffle,epoches,batch_size):
    features = {key:np.array([value]).T for key,value in dict(features).items()}
    ds = tf.data.Dataset.from_tensor_slices((features,target))
    ds = ds.batch(batch_size).repeat(epoches)
    if shuffle:
        ds = ds.shuffle(10000)
    feature,lable = tf.compat.v1.data.make_one_shot_iterator(ds).get_next()
    return feature,lable
#model define and trainning process definition
def train_DNN_model(feature_trainning,target_trainning,feature_validation,target_validation, steps):
    my_optimizer = tf.optimizers.SGD(learning_rate = 0.001, clipnorm = 5)
    DNN_regressor = tf.estimator.DNNRegressor(feature_columns = construct_feature_columns(feature_trainning),
                                               optimizer = my_optimizer,
                                               hidden_units = [10,10])
    input_func_trainning = lambda: input_func(feature_trainning, target_trainning, shuffle=True, epoches=None, batch_size=100)
    DNN_regressor.train(input_fn = input_func_trainning,
                        steps = steps)
    return DNN_regressor

"Step 3: making predictions"
DNN_regressor = train_DNN_model(features_trainning, target_trainning, features_validation, target_validation, 2000)

#datasource for predictions
#predicting trainning dataset
input_fn_trainning = lambda: input_func(features = features_trainning, target=target_trainning, shuffle=False, epoches=1, batch_size=1)
predictions_trainning = DNN_regressor.predict(input_fn = input_fn_trainning)
#extract and format the dataset
predictions_trainning = np.array([item["predictions"][0] for item in predictions_trainning])
#MSE
mse = metrics.mean_squared_error(target_trainning, predictions_trainning)

咱們可以看出來,它的整個流程還是一樣,只在一個地方後其他的模型訓練過程不一樣,那就是選擇TensorFlow的estimator中的模型不一樣而已,例如上面的是一個線性的DNN,咱們選擇的就是下面的DNNRegression

DNN_regressor = tf.estimator.DNNRegressor(feature_columns = construct_feature_columns(feature_trainning),
                                               optimizer = my_optimizer,
                                               hidden_units = [10,10])

注意上面定義模型的引數,它多了一個hidden_units引數,這就是使用者自定義的hidden layers的部分,如果咱們的結果不理想,咱們可以適當的增加hidden_units的數量。上面的是一個線性的DNN的模型定義,那麼如果咱們的是non-linear的模型,例如classification,咱們如何定義呢?請看下面的程式碼

    DNN_classifier = tf.estimator.DNNClassifier(hidden_units = [100,100],
                                                feature_columns = configure_column_features(),
                                                optimizer = my_optimizer,
                                                n_classes = 10,
                                                activation_fn=tf.nn.relu)

如果咱們的模型是non-linear的classification problem,那麼咱們就選擇estimator中的DNNClassifier模型,這裡咱們可以看出它也增加了很多引數,n_classes是說明咱們的資料一共有多少個classes,預設值是2;activation_fn是選擇的relu; 這些值都是使用者根據實際情況自定義的,我這裡的只是一個最簡單的演示。其實他還有很多很多引數可以定義,大家自己去看文件根據實際的情況來定義。

  • 神經網路之Keras應用

  上面咱們介紹了用TensorFlow的estimator來定義和訓練神經網路,但是在實際中有一個更加強大的框架來專門處理深度學習的問題,那就是無敵的Keras。Keras自己是一個獨立的框架,專門用來處理深度學習的相關問題,咱們可以直接下載並且匯入它的元件進行應用;但是呢,無敵的TensorFlow早就為了方便大家而提前將Keras匯入到了TensorFlow的tf.keras這個模組中了,所以大家也不需要單獨的來匯入了,直接就用TensorFlow中的tf.keras模組就能實現幾乎所有的Keras的功能。首先,咱們也是來看一下用Keras最簡單的方式搭建一個DNN並且訓練這個神經網路。

第一步:網路結構搭建

#import keras
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np

##1.build a sequential network
model = keras.Sequential()
#add a full-connected and dense layer
model.add(layers.Dense(64,activation ='relu',input_shape=(32,)))
#add an another layer with l2 regularizer
model.add(layers.Dense(50, 
                       activation = 'sigmoid', 
                       kernel_regularizer = keras.regularizers.l2(0.01),
                       bias_regularizer = keras.regularizers.l2(0.02),
                       bias_initializer = keras.initializers.Ones(),
                       kernel_initializer = 'glorot_uniform'
                       )
          )
#add another layer with l2 and l1 regularizer
model.add(layers.Dense(40, activation = 'relu', kernel_regularizer = keras.regularizers.l2(0.01), bias_regularizer = keras.regularizers.l1(0.01)))
#add another layer with l1,l2 regularizer and bias/kernel initializer
model.add(layers.Dense(10,activation = 'softmax'))
"""

首先咱們初始化咱們神經網路的layers, 咱們的網路有多少的layers,咱們就初始化多少個Dense layer例項。然後將這些layers按照順序的一次加入到咱們的model物件中。這裡每一個Dense layer咱們都可以使用者自定義很多的引數,我在上面的例子中也展示了很多種例子,例如有:activation, regularizer, initializer等等很多,如果大家去看他的文件,大家會看到更多的引數,但是在實際中,咱們主要就是設定上面的例子中展示的一些引數。但是這裡有一個小細節大家一定要注意,否很容易出現runtime error,而且非常難找到原因,那就是bias_initializer和kernel_initializer的選擇,這裡並不是隨便選擇一個initializer就行的,首先kernel_initializer是一個matrix,所以它所選擇的initializer必須得是返回matrix的,例如上面例子中的glorot_uniform等等,而bias_initializer則是一個一維的vector!!!記住bias是vector而不是matrix,所以它所選擇的initializer則必須得是返回一維的vector的的initializer,而不能是glorot_uniform, othogonal等initializer。這裡的細節很容易讓人忽略,而一旦出錯卻很難找到原因的。另外一點,input layer是不需要定義的,Keras是自動的會把咱們的input layer加進去的,但是output layer是需要咱們手動定義並且加上去的。所以上面的模型結構是一個input layer, 三個hidden layers和一個output layer。咱們也可以通過model.summary()的方法來檢查咱們的模型結構,如下所示

 

 上面就是model.summary()返回的結果,它預設也沒有顯示input layer。

第二步:配置上面定義的模型結構

model.compile(
    optimizer = keras.optimizers.Adam(0.01),
    loss = 'mse',
    metrics = ['mae'])

這一步主要是給上面定義的網路模型配置一些基本的資訊,例如optimizer, loss function和metrics這些模型必要的一些資訊。這裡跟咱們之前講的其他的一些基本模型都是一樣的,這裡就不在贅述了,如果不知道就看我前面的部落格。

第三部: 資料準備

這部分內容呢既可以放在咱們的第一步,也可以放在咱們的網路模型都定義好了之後,這裡我就隨機產生幾個資料當做咱們的資料模型,方便咱們後面內容的演示

data = np.random.random((1000,32))
labels = np.random.random((1000,10))
val_data = np.random.random((100,32))
val_labels = np.random.random((100,10))


dataset = tf.data.Dataset.from_tensor_slices((data,labels))
dataset = dataset.batch(32)
val_dataset = tf.data.Dataset.from_tensor_slices((val_data,val_labels))
val_dataset = val_dataset.batch(32)

第四步:模型訓練

#trainning a model from dataset
model.fit(dataset,epochs = 10, validation_data=val_dataset)

這裡訓練資料的時候,咱們的資料既可以是numpy array也可以是dataset,因為我個人習慣的問題,我傾向於是有dataset的資料來訓練,所以我上面的例子也是用的dataset。上面epochs的引數是說明咱們的模型訓練的時候,咱們一共重複咱們的資料集多少次。

第五步:predict 和 evaluation

#prediction
model.predict(data)

#evaluation
model.evaluate(dataset)
  • Keras Functional APIs (save & load model)

上面只是展示瞭如何用Keras搭建並且訓練一個最簡單的神經網路,那麼實際中咱們遇到的會遇到一些其他的需求,例如當咱們的模型訓練後,咱們如何儲存這個模型呢?如何儲存咱們訓練得來的weights呢?如何載入咱們儲存在本地的模型呢?如何載入咱們的weights呢?這些都是咱們肯定會遇到的問題。那麼這些功能性的API都是如何應用呢?咱們這裡就一個個的給大家介紹一下。

第一:儲存/載入 整個模型

#save a entire model
model.save("C:/Users/tangx/OneDrive/Desktop/path_to_my_model.h5")

#load a entire model model = keras.models.load_model("C:/Users/tangx/OneDrive/Desktop/path_to_my_model.h5")

上面第一步就是將咱們訓練的模型(包括模型的結構和weights, bias等所有的資訊)都儲存在本地的指定的位置。第二句程式碼就是載入整個咱們的本地的模型,當然了,這個載入後的模型也是包括了所有的資訊,包括了模型結構,weights和bias所有的資訊。

第二:儲存/載入 咱們的weights和bias

在有些情況下,咱們只想載入咱們訓練出來的weights(包括了bias啊),那麼這種情況下,咱們如何儲存呢?看下面的程式碼

#only save weights
model.save_weights("C:/Users/tangx/OneDrive/Desktop/model_wights")

上面是Keras提供的將咱們model的weights(包括bias)儲存在本地的方式,注意哦, 這裡只是儲存了weights哦,並沒有這個model的結構哦,那麼咱們如何完整的載入這個模型呢?光有weights而沒有網路結構的話可是沒有用的哦。那麼接下來看一下如何通過載入weights來載入整個模型資訊呢,首先咱們得知道這個weights所對應的網路結構,然後重新定義並且初始化一個相對應的神經網路,相當於獲取的一個“空模型“, 然後用下面的程式碼將weights填充到這個“空模型”中

#restore the model's state, which requires a model with same architecture
model.load_weights("C:/Users/tangx/OneDrive/Desktop/model_wights")

這之後,相當於給咱們的model填充了模型內容,從而咱們的model就可以進行正常的操作了,例如predict,evaluate等等。

第三: 儲存/載入 網路結構和配置(serialize a model)

從上面的內容咱們可以知道,如果咱們只儲存了weights的話,咱們在下次載入整個模型的時候,咱們還得自己重新定義並且例項化一個網路結構和配置,然後咱們才能載入咱們的weights,從而讓這個模型可用。可以實際中咱們也可以單獨的只儲存/載入這個模型的結構和配置。那麼咱們如何做呢?看下面程式碼演示

#save and recreate a model's configuration without any weights(serilizes a model to json format)
json_string = model.to_json()

#recreate a model archetechture and configuration from json string without any weights
fresh_model = keras.models.model_from_json(json_string)

上面第一句程式碼呢就是將咱們的模型架構和配置資訊轉成json的資料結構儲存起來,記住啊,這裡只儲存了網路架構和配置資訊,並不包括訓練得來的weights,這裡的過程也稱作model serialization。第二句程式碼就是從咱們序列化json資料格式中,載入咱們的網路結構和網路配置資訊。從而咱們也可以直接將這個fresh_model用來load_weights, 從而成為一個完成的模