- 概述
神經網絡是深度學習的基礎,它在人工智能中有着非常廣泛的應用,它既可以應用於咱們前面的章節所說的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, 從而成為一個完成的模型。