計算圖中的操作
代碼實現:
import numpy as np import tensorflow.compat.v1 as tf tf.disable_v2_behavior() # 使用靜態圖模式運行以下代碼 assert tf.__version__.startswith('2.') sess = tf.Session() x_vals = np.array([1., 3., 5., 7., 9.]) x_data = tf.placeholder(dtype=tf.float32) m_const = tf.constant(3.) my_product = tf.multiply(x_data, m_const) for x_val in x_vals: print(sess.run(my_product, feed_dict={x_data:x_val}))
執行結果:
Tensorflow的嵌入Layer
代碼實現:
import numpy as np import tensorflow.compat.v1 as tf tf.disable_v2_behavior() # 使用靜態圖模式運行以下代碼 assert tf.__version__.startswith('2.') sess = tf.Session() # 創建數據和占位符 my_array = np.array([[1., 3., 5., 7., 9.], [-2., 0., 2., 4., 6.], [-6., -3., 0., 3., 6.]]) x_vals = np.array([my_array, my_array+1]) x_data = tf.placeholder(shape=(3,5), dtype=tf.float32) # 創建常量矩陣 m1 = tf.constant([[1.], [0.], [-1.], [2.], [4.]]) # 5x1矩陣 m2 = tf.constant([[2.]]) # 1x1矩陣 a1 = tf.constant([[10.]]) # 1x1矩陣 # 聲明操作, 表示成計算圖 prod1 = tf.matmul(x_data, m1) # 3x5 乘以 5x1 = 3x1 prod2 = tf.matmul(prod1, m2) # 3x1 乘以標量 = 3x1 add1 = tf.add(prod2, a1) # 賦值 for x_val in x_vals: print(sess.run(add1, feed_dict={x_data: x_val})) sess.close()
執行結果:
注意:對於事先不知道的維度大小,可以用None代替。例如:
x_data = tf.placeholder(dtype=tf.float32, shape=(3,None))
Tensorflow的多層layer
對2D圖像數據進行滑動窗口平均,然后通過自定義操作層Layer返回結果。
代碼實現:
import numpy as np import tensorflow.compat.v1 as tf tf.disable_v2_behavior() # 使用靜態圖模式運行以下代碼 assert tf.__version__.startswith('2.') sess = tf.Session() # 創建2D圖像,4X4像素。 x_shape = [1, 4, 4, 1] # [batch, in_height, in_width, in_channels] tensorflow處理(圖片數量,高度、寬度、顏色)思維圖像數據 x_val = np.random.uniform(size=x_shape) # 占位符 x_data = tf.placeholder(dtype=tf.float32, shape=x_shape) # 創建滑動窗口 my_filter = tf.constant(0.25, shape=[2,2,1,1]) # [filter_height, filter_width, in_channels, out_channels] my_strides = [1, 2, 2, 1] # 長寬方向滑動步長均為2 mov_avg_layer = tf.nn.conv2d(x_data, my_filter, my_strides, padding='SAME', name='Moving_avg_Window') # 定義一個自定義Layer,操作上一步滑動窗口的2x2的返回值。 # 自定義函數將輸入張量乘以一個2X2的矩陣張量,然后每個元素加1.因為矩陣乘法只計算二維矩陣,所以裁剪多余維度(大小為1的維度)。Tensorflow通過內建函數squeeze()函數裁剪。 def custom_layer(input_matrix): input_matrix_squeezed = tf.squeeze(input_matrix) A = tf.constant([[1., 2.], [-1., 3.]]) b = tf.constant(1., shape=[2,2]) temp1 = tf.matmul(A, input_matrix_squeezed) temp = tf.add(temp1, b) # Ax+b return (tf.sigmoid(temp)) # 把剛剛定義的Layer加入到計算圖中,並用tf.name_scope()命名Layer名稱。 with tf.name_scope('Custom_Layer') as scope: custom_layer1 = custom_layer(mov_avg_layer) # 傳入數據,執行 print(sess.run(custom_layer1, feed_dict={x_data:x_val})) # 關閉會話,釋放資源 sess.close()
執行結果:
Tensorflow實現損失函數
損失函數對機器學習來講非常重要,它度量模型輸出值與目標值之間的差距。
回歸問題常用損失函數
代碼實現:
import numpy as np import matplotlib.pyplot as plt import tensorflow.compat.v1 as tf tf.disable_v2_behavior() # 使用靜態圖模式運行以下代碼 assert tf.__version__.startswith('2.') sess = tf.Session() # 創建2D圖像,4X4像素。 x_shape = [1, 4, 4, 1] # [batch, in_height, in_width, in_channels] tensorflow處理(圖片數量,高度、寬度、顏色)思維圖像數據 x_val = np.random.uniform(size=x_shape) # 占位符 x_data = tf.placeholder(dtype=tf.float32, shape=x_shape) # 創建滑動窗口 my_filter = tf.constant(0.25, shape=[2,2,1,1]) # [filter_height, filter_width, in_channels, out_channels] my_strides = [1, 2, 2, 1] # 長寬方向滑動步長均為2 mov_avg_layer = tf.nn.conv2d(x_data, my_filter, my_strides, padding='SAME', name='Moving_avg_Window') # 定義一個自定義Layer,操作上一步滑動窗口的2x2的返回值。 # 自定義函數將輸入張量乘以一個2X2的矩陣張量,然后每個元素加1.因為矩陣乘法只計算二維矩陣,所以裁剪多余維度(大小為1的維度)。Tensorflow通過內建函數squeeze()函數裁剪。 def custom_layer(input_matrix): input_matrix_squeezed = tf.squeeze(input_matrix) A = tf.constant([[1., 2.], [-1., 3.]]) b = tf.constant(1., shape=[2,2]) temp1 = tf.matmul(A, input_matrix_squeezed) temp = tf.add(temp1, b) # Ax+b return (tf.sigmoid(temp)) # 把剛剛定義的Layer加入到計算圖中,並用tf.name_scope()命名Layer名稱。 with tf.name_scope('Custom_Layer') as scope: custom_layer1 = custom_layer(mov_avg_layer) # 傳入數據,執行 print(sess.run(custom_layer1, feed_dict={x_data:x_val})) # 關閉會話,釋放資源 sess.close()
執行結果:
關於Huber函數
L1正則在目標值處不平滑。L2正則在異常值處過大,會放大異常值的影響。因此,Huber函數在目標值附近(小於delta)是L2正則,在大於delta處是L1正則。解決上述問題。
參考
Huber損失函數
繪圖展示回歸算法損失函數
x_array = sess.run(x_vals) plt.plot(x_array, l2_y_out, 'b-', label='L2 Loss') plt.plot(x_array, l1_y_out, 'r--', label='L1 Loss') plt.plot(x_array, phuber1_y_out, 'k-', label='P-Huber Loss (0.25)') plt.plot(x_array, phuber2_y_out, 'g:', label='P-Huber Loss (0.5)') plt.ylim(-0.2, 0.4) plt.legend(loc='lower right', prop={'size':11}) plt.show()
回歸算法損失函數入下圖所示:

可以看出,L2損失函數離0點越遠,損失會被2次方放大。L1損失函數在0點處不可導。P-Huber損失函數則在0點出是近似的L2損失函數,在遠離零點處是近似的斜線。既解決了L1損失梯度下降算法可能不收斂的問題,又減少了異常值的影響。
分類問題常用損失函數
# 創建數據 x_vals = tf.linspace(-3., 5., 500) target = tf.constant(1.) targets = tf.fill([500,], 1) # Hinge 損失函數:評估支持向量機算法,有時也用來評估神經網絡算法。 hinge_y_vals = tf.maximum(0., 1. - tf.multiply(target, x_vals)) hinge_y_out = sess.run(hinge_y_vals) # 兩類交叉熵損失函數(cross-entropy loss): xentropy_y_vals = -tf.multiply(target, tf.log(x_vals)) - tf.multiply((1.-target), tf.log(1. - x_vals)) xentropy_y_out = sess.run(xentropy_y_vals) # Sigmoid交叉熵損失函數:與上面的交叉熵損失函數非常類似,只是先將x_vals值通過sigmoid轉換在計算交叉熵。 xentropy_sigmoid_y_vals = tf.nn.sigmoid_cross_entropy_with_logits(labels = target, logits = x_vals) xentropy_sigmoid_y_out = sess.run(xentropy_sigmoid_y_vals) # 加權交叉熵損失函數:Sigmoid交叉熵損失函數的加權,對正目標加權。 weight = tf.constant(0.5) xentropy_weighted_y_vals = tf.nn.weighted_cross_entropy_with_logits(targets = targets, logits = x_vals, pos_weight = weight) xentropy_weighted_y_out = sess.run(xentropy_weighted_y_vals) # Softmax交叉熵損失函數:作用於非歸一化的輸出結果,只針對單個目標分類的計算損失。 unsacled_logits = tf.constant([[1., -3., 10.]]) target_dist = tf.constant([[0.1, 0.02, 0.88]]) softmax_xentropy = tf.nn.sotfmax_entropy_with_logits(logits = unscaled_logits, labels = target_dist) print(sess.run(softmax_xentropy) # 稀疏Softmax交叉熵損失函數 unscaled_logits = tf.constant([[1., -3., 10.]]) sparse_target_dist = tf.constant([2]) sparese_xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits = unscaled_logits, labels = sparse_target_dist) print(sess.run(sparse_xentropy))
關於交叉熵的說明
1 信息量
假設我們聽到了兩件事:
A:巴西隊進入了2018世界杯決賽圈;B:中國隊進入了2018世界杯決賽圈。
直覺上來說,事件B比事件A包含的信息量大,因為小概率事件發生了,一定是有些我們不知道的東西。因此,這樣定義信息量:
假設X是一個離散型隨機變量,其取值集合為,概率分布函數
,則定義事件
的信息量為:
信息量函數如下圖所示:

可以看出-log(x)函數刻畫的信息量,x越接近零越大,越接近1越小。這里便可以理解二分類問題中的logistics回歸的損失函數-y*log(y_hat)-(1-y)*log(1-y_hat)
, 它會給錯誤分類很大的懲罰。比如,在y=1是,y_hat如果接近0,會導致loss很大,同樣y=0時,y_hat接近1,也會導致loss很大。因此在最小化Loss函數時,會盡可能的將y=1的y_hat預測成1,y=0的y_hat預測為0。
2 熵
對於某個事件,有n中可能,那么求其n中可能的信息量的期望,便是熵。即熵:
3 相對熵(KL散度)
相對熵又稱KL散度,如果對於隨機變量x有兩個概率分布P(x)和Q(x),我們可以使用KL散度來衡量兩個分布的差異。KL散度的計算公式:
可見當q和p的分布越接近時,KL散度越小。當q和p為相同的分布是,KL散度為0。
4 交叉熵
在機器學習里,p是樣本的真實分布。比如貓狗鼠的分類問題,一個樣本點(是貓)的實際分布是(1,0,0)。而q是模型預測分布,比如(0.8,0.1,0.1)。那么我們模型訓練的目標可以設置為是的p、q的KL散度最小化,即q盡可能接近真實的p分布。
而:
其中,,是p分布的熵;
是p和q的交叉熵。由於p是樣本的真實分布,所以H(p(x))是一個常數值。那么KL散度最小化也就等價於交叉熵的最小化。
關於Softmax損失函數和稀疏Softmax損失函數的說明
首先,Softmax和系數Softmax損失函數最大的不同是輸入的labels不同。對於某一個樣本點,Softmax損失函數輸入的labels是各分類的實際概率,例如貓狗鼠分類,就是(1,0,0)這樣的向量。對於m各樣本點,輸入的labels則是一個m*3的矩陣。而稀疏Softmax,輸入labels是實際所屬類別的index,比如這里是(1,0,0),則輸入就是0。m個樣本點,輸入是m維向量,向量里面的內容是該樣本點所屬類別的索引。
對於單個樣本點,多分類的交叉熵如下:, 其中,
是樣本點i各類別的實際概率分布(例如(1,0,0)),
是預測概率分布(例如(0.7,0.2,0.1))。對於Softmax函數,我們需要計算log(0.7),log(0.2),log(0.1)。但由於log(0.2),log(0.1)前面乘以的實際的
是零,實際上是沒有必要計算的。這樣,Softmax損失函數就會造成算力的浪費,尤其深度學習的數據量非常大,這種浪費往往不能忍受。而稀疏Softmax則只計算
時的
(這里即log(0.7),可以大大的提升計算性能。因此,對於這種實際分布只有一個1,其他全為零的排他性分類問題,Softmax和稀疏Softmax損失函數沒有本質上的區別,只是后者在計算性能上有一個優化。
評價機器學習模型的其他指標
指標 | 描述 |
---|---|
![]() |
對簡單線性模型來講,用於度量因變量中可由自變量解釋部分所占比例。 |
RMSE (平均方差) | 對連續模型來講,是度量預測值和觀測值之差的樣本標准差。 |
混淆矩陣(Confusion matrix) | 對分類模型來講,以矩陣形式將數據集中的記錄按照真實的類別與預測類別兩個標准進行分析匯總,其列代表預測類別,行代表實際類別。理想情況下,混淆矩陣是對角矩陣。 |
查全率(Recall) | 正類中有多少被正確預測(正類中被正確預測為正類的比例)。 |
查准率(Precision) | 被預測為正類的樣本中有多少是真的正類。 |
F值(F-score) | 對於分類模型來講,F值是查全率和查准率的調和平均數 |
Tensorflow實現反向傳播
簡單的回歸例子
這個例子很簡單,是一個一元線性回歸模型y=Ax。x是均值為1,標准差0.1的標准正態分布生成的100個樣本點。y都是10。這樣回歸參數A應該是10左右。
代碼實現:
import numpy as np import matplotlib.pyplot as plt import tensorflow.compat.v1 as tf tf.disable_v2_behavior() # 使用靜態圖模式運行以下代碼 assert tf.__version__.startswith('2.') # 2 創建會話 sess = tf.Session() # 3 生成數據,創建占位符和變量 x_vals = np.random.normal(1, 0.1, 100) # 均值為1,標准差為0.1,100個樣本點 y_vals = np.repeat(10., 100) # 10 x_data = tf.placeholder(shape=[1], dtype=tf.float32) y_target = tf.placeholder(shape=[1], dtype=tf.float32) A = tf.Variable(tf.random_normal(shape=[1])) # 4 模型 my_output = tf.multiply(x_data, A) # 5 增加L2正則損失函數 loss = tf.square(my_output - y_target) # 這里損失函數是單個樣本點的損失,因為一次訓練只隨機選擇一個樣本點 # 6 初始化變量 init = tf.initialize_all_variables() sess.run(init) # 7 聲明變量的優化器。設置學習率。 my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.02) train_step = my_opt.minimize(loss) # 8 訓練算法 for i in range(100): rand_index = np.random.choice(100) # 隨機選擇一個樣本點 rand_x = [x_vals[rand_index]] rand_y = [y_vals[rand_index]] sess.run(train_step, feed_dict={x_data:rand_x, y_target:rand_y}) if (i+1)%25 == 0: print('Step #' + str(i) + ' A = ' + str(sess.run(A))) print('Loss = ' + str(sess.run(loss, feed_dict={x_data:rand_x, y_target:rand_y}))) # 9 關閉會話 sess.close()
執行結果: 
簡單的分類例子
這是一個簡單的二分類問題。100個樣本點,其中50個由N(-1,1)生成,標簽為0,50個由N(3,1)生成,標簽為1。用sigmoid函數實現二分類問題。模型設置為sigmoid(A+x)。兩類數據中心點的中點是1,可見A應該在-1附近。
代碼實現:
import numpy as np import matplotlib.pyplot as plt import tensorflow.compat.v1 as tf tf.disable_v2_behavior() # 使用靜態圖模式運行以下代碼 assert tf.__version__.startswith('2.') # 2 創建會話 sess = tf.Session() # 3 生成數據,創建變量和占位符 x_vals = np.concatenate((np.random.normal(-1, 1, 50), np.random.normal(3, 1, 50))) y_vals = np.concatenate((np.repeat(0., 50), np.repeat(1., 50))) x_data = tf.placeholder(shape=[1], dtype=tf.float32) y_target = tf.placeholder(shape=[1], dtype=tf.float32) A = tf.Variable(tf.random_normal(mean=10, shape=[1])) # 模型。y_pre = sigmoid(A+x)。 # 這里不必封裝sigmoid函數,因為損失函數中會實現此功能 my_output = tf.add(x_data, A) # 增加一個批量維度,使其滿足損失函數的輸入要求。 my_output_expanded = tf.expand_dims(my_output, 0) y_target_expanded = tf.expand_dims(y_target, 0) # 初始化變量A init = tf.initialize_all_variables() sess.run(init) # 損失函數 xentropy = tf.nn.sigmoid_cross_entropy_with_logits(logits=my_output_expanded, labels=y_target_expanded) # 優化器 my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.05) train_step = my_opt.minimize(xentropy) # 迭代訓練 for i in range(1400): rand_index = np.random.choice(100) rand_x = [x_vals[rand_index]] rand_y = [y_vals[rand_index]] sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y}) if (i + 1) % 200 == 0: print('Step #' + str(i + 1) + ' A= ' + str(sess.run(A))) print('Loss = ' + str(sess.run(xentropy, feed_dict={x_data: rand_x, y_target: rand_y}))) # 關閉會話 sess.close()
# 輸出結果如下: