第八屆中國Python開發者大會PyConChina2018,由PyChina.org發起,由來自CPyUG/TopGeek等社群的30位組織者,近150位志願者在北京、上海、深圳、杭州、成都等城市舉辦。致力於推動各類Python相關的技術在網際網路、企業應用等領域的研發和應用。
程式碼醫生工作室有幸接受邀請,參加了這次會議的北京站專場。在會上主要分享了《人工智慧實戰案例分享-影像處理與數值分析》。
會上分享的一些案例主要是來源於《python帶我起飛——入門、進階、商業實戰》一書與《深度學習之TensorFlow:入門、原理與進階實戰》一書。另外,還擴充了若干其它案例。在本文作為補充,將會上分享的其它案例以詳細的圖文方式補充進來,並提供原始碼。共分為4期連載。
-
用slim呼叫PNASNet模型
-
用slim微調PNASNet模型
-
用對抗樣本攻擊PNASNet模型
-
惡意域名檢測例項
用FGSM生成樣本來攻擊PNASNet網路,令其將狗識別成盤子
本例通過使用FGSM的方式,來生成攻擊樣本。對PNASNet發起攻擊,使其輸出的識別結果,產生錯誤。
案例描述
先用一張哈士奇狗的照片,輸入帶有預編譯模型PNASNet網路。觀察其返回結果。
通過梯度下降演算法訓練一個模擬樣本。待模擬樣本生成完成之後,輸入PNASNet令其輸出識別結果為盤子。
該案例所實現的FGSM原理很簡單:將輸入的圖片當作訓練引數。通過優化器不斷的迭代,來將圖片逐漸改造成輸入模型認為使盤子的樣子。下面就來演示其實現過程。
程式碼實現:建立PNASNet模型
在程式碼“3-1 使用AI模型來識別影像.py”中,介紹了一個使用PNASNet網路的預編譯模型,進行圖片識別的例子。本案例就在該程式碼基礎上實現。首先“3-1 使用AI模型來識別影像.py”程式碼檔案的整個工作環境複製一份。並編寫pnasnetfun函式,實現模型的建立。完整的程式碼如下:
線上性迴歸模型中新增指定節點到檢查點檔案
1 import sys #初始化環境變數
2 nets_path = r`slim`
3 if nets_path not in sys.path:
4 sys.path.insert(0,nets_path)
5 else:
6 print(`already add slim`)
7
8 import tensorflow as tf #引入標頭檔案
9 from nets.nasnet import pnasnet
10 import numpy as np
11 from tensorflow.python.keras.preprocessing import image
12
13 import matplotlib as mpl
14 import matplotlib.pyplot as plt
15 mpl.rcParams[`font.sans-serif`]=[`SimHei`]#用來正常顯示中文標籤
16 mpl.rcParams[`font.family`] = `STSong`
17 mpl.rcParams[`font.size`] = 15
18
19 slim = tf.contrib.slim
20 arg_scope = tf.contrib.framework.arg_scope
21
22 tf.reset_default_graph()
23 image_size = pnasnet.build_pnasnet_large.default_image_size #獲得圖片輸入尺寸
24 LANG = `ch` #使用中文標籤
25
26 if LANG==`ch`:
27 def getone(onestr):
28 return onestr.replace(`,`,` `).replace(`
`,``)
29
30 with open(`中文標籤.csv`,`r+`) as f: #開啟檔案
31 labelnames =list( map(getone,list(f)) )
32 print(len(labelnames),type(labelnames),labelnames[:5]) #顯示輸出中文標籤
33 else:
34 from datasets import imagenet
35 labelnames = imagenet.create_readable_names_for_imagenet_labels() #獲得資料集標籤
36 print(len(labelnames),labelnames[:5]) #顯示輸出標籤
37
38 def pnasnetfun(input_imgs,reuse ):
39 preprocessed = tf.subtract(tf.multiply(tf.expand_dims(input_imgs, 0), 2.0), 1.0)
40 arg_scope = pnasnet.pnasnet_large_arg_scope() #獲得模型名稱空間
41
42 with slim.arg_scope(arg_scope): #建立PNASNet模型
43 with slim.arg_scope([slim.conv2d,
44 slim.batch_norm, slim.fully_connected,
45 slim.separable_conv2d],reuse=reuse):
46
47 logits, end_points = pnasnet.build_pnasnet_large(preprocessed,num_classes = 1001, is_training=False)
48 prob = end_points[`Predictions`]
49 return logits, prob複製程式碼
程式碼13~17行在《深度學習之TensorFlow:入門、原理與進階實戰》9.7.4節也用過,是支援中文顯示的作用。程式碼43行的作用,是支援PNASNet模型的變數複用功能。
注意:
在建立模型時,需要將其設為不可訓練(程式碼47行)。這樣才能保證,在後面12.2.3節通過訓練生成攻擊樣本時,PNASNet模型不會有變化。
程式碼實現:搭建輸入層並載入圖片,復現PNASNet預測效果
在本案例中,沒有使用佔位符來建立輸入層,而是使用了張量。使用tf.Variable定義的張量與佔位符的效果一樣,同樣可以支援注入機制。這麼做的目的是為了在後面製造攻擊樣本時使用。接著是載入圖片、載入預編譯模型、將圖片注入到預編譯模型中的操作。並最終視覺化輸出預測結果。完整的程式碼如下:
程式碼12-1 線上性迴歸模型中新增指定節點到檢查點檔案(續)
50 input_imgs = tf.Variable(tf.zeros((image_size, image_size, 3)))
51 logits, probs = pnasnetfun(input_imgs,reuse=False)
52 checkpoint_file = r`pnasnet-5_large_2017_12_13model.ckpt` #定義模型路徑
53 variables_to_restore = slim.get_variables_to_restore()
54 init_fn = slim.assign_from_checkpoint_fn(checkpoint_file, variables_to_restore,ignore_missing_vars=True)
55
56 sess = tf.InteractiveSession() #建立會話
57 init_fn(sess) #載入模型
58
59 img_path = `./dog.jpg` #載入圖片
60 img = image.load_img(img_path, target_size=(image_size, image_size))
61 img = (np.asarray(img) / 255.0).astype(np.float32)
62
63 def showresult(img,p): #視覺化模型輸出結果
64 fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 8))
65 fig.sca(ax1)
66
67 ax1.axis(`off`)
68 ax1.imshow(img)
69 fig.sca(ax1)
70
71 top10 = list((-p).argsort()[:10])
72 lab= [labelnames[i][:15] for i in top10]
73 topprobs = p[top10]
74 print(list(zip(top10,lab,topprobs)))
75
76 barlist = ax2.bar(range(10), topprobs)
77
78 barlist[0].set_color(`g`)
79 plt.sca(ax2)
80 plt.ylim([0, 1.1])
81 plt.xticks(range(10), lab, rotation=`vertical`)
82 fig.subplots_adjust(bottom=0.2)
83 plt.show()
84
85 p = sess.run(probs, feed_dict={input_imgs: img})[0]
86 showresult(img,p)
複製程式碼
程式碼執行後,如下結果:
[(249, `愛斯基摩犬 哈士奇 `, 0.35189062), (251, `哈士奇 `, 0.34352344), (250, `雪橇犬 阿拉斯加愛斯基摩狗 `, 0.007250515), (271, `白狼 北極狼 `, 0.0034629034), (175, `挪威獵犬 `, 0.0028237076), (538, `狗拉雪橇 `, 0.0025286602), (270, `灰狼 `, 0.0022800271), (274, `澳洲野狗 澳大利亞野犬 `, 0.0018357899), (254, `巴辛吉狗 `, 0.0015468642), (280, `北極狐狸 白狐狸 `, 0.0009330675)]
並得到視覺化圖片,如圖12-1:
圖12-1 PNASNet模型輸出
圖12-1中,左側為輸入的圖片,右側為其預測的結果。可以看到模型能夠成功的預測出該圖片內容是一隻哈士奇狗。
程式碼實現:定義圖片變化範圍,並通過人工校驗的方式調整引數
在製作樣本時,不能讓圖片的變化太大。還要讓圖片在人眼看上去能夠接收才行。這裡需要手動設定閾值,限制圖片的變化範圍。然後將生成的圖片顯示出來,由人眼判斷圖片是否正常可用。確保沒有失真。完整的程式碼如下:
程式碼12-1 線上性迴歸模型中新增指定節點到檢查點檔案(續)
1 def floatarr_to_img(floatarr): #將浮點型轉化為圖片
2 floatarr=np.asarray(floatarr*255)
3 floatarr[floatarr>255]=255
4 floatarr[floatarr<0]=0
5 return floatarr.astype(np.uint8)
6
7 x = tf.placeholder(tf.float32, (image_size, image_size, 3)) #定義佔位符
8 assign_op = tf.assign(input_imgs, x) #為input_imgs賦值
9 sess.run( assign_op, feed_dict={x: img})
10
11 below = input_imgs - 2.0/255.0 #定義圖片的變化範圍
12 above = input_imgs + 2.0/255.0
13
14 belowv,abovev = sess.run( [below,above]) #生成閾值圖片
15
16 plt.imshow(floatarr_to_img(belowv)) #顯示圖片,用於人眼驗證
17 plt.show()
18 plt.imshow(floatarr_to_img(abovev))
19 plt.show()複製程式碼
在程式碼第8行,為input_imgs賦值之後,才可以使用。因為到目前為止input_imgs還沒有初始化,必須呼叫input_imgs.initializer或tf.assign為其賦值。這部分知識與《深度學習之TensorFlow:入門、原理與進階實戰》一書的3.3.8節相關。
這裡設定的閾值是:每個畫素上下變化最大不超過2(見程式碼11、12行)。經過程式碼執行後,輸出的圖片如圖12-2、圖12-3。
如圖12-2、12-3所示,該圖片完全在人類接收範圍。一眼看去,就是一隻哈士奇狗。
程式碼實現:用梯度下降方式生成攻擊樣本
與正常的網路模型訓練目標不同,這裡的訓練目標不是模型中的權重,而是輸入模型的張量。在12.2.1節中,生成模型的同時,已經將模型固定好(設為不可訓練)。這樣在模型訓練的過程中,反向梯度傳播的最終作用值就是輸入的張量input_imgs了。
訓練步驟與正常的網路模型一致,設定一個其它類別的標籤。建立loss節點,令每次優化時,模型的輸出都接近於設定的標籤類別。完整的程式碼如下:
程式碼12-1 線上性迴歸模型中新增指定節點到檢查點檔案(續)
20 label_target = 880 #設定一個其它類別的目標標籤
21 label =tf.constant(label_target)
22 #定義loss節點
23 loss = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=[label])
24 learning_rate = 1e-1 #定義學習率
25 optim_step = tf.train.GradientDescentOptimizer( #定義優化器
26 learning_rate).minimize(loss, var_list=[input_imgs])
27
28 #將調整後的圖片,按照指定閾值截斷
29 projected = tf.clip_by_value(tf.clip_by_value(input_imgs, below, above), 0, 1)
30 with tf.control_dependencies([projected]):
31 project_step = tf.assign(input_imgs, projected)
32
33 demo_steps = 400 #定義迭代次數
34 for i in range(demo_steps): #開始訓練
35 _, loss_value = sess.run([optim_step, loss])
36 sess.run(project_step)
37
38 if (i+1) % 10 == 0: #輸出訓練狀態
39 print(`step %d, loss=%g` % (i+1, loss_value))
40 if loss_value<0.02: #達到標準後,提前結束
41 break
複製程式碼
程式碼執行後,輸出如下結果:
step 10, loss=5.29815
step 20, loss=0.00202808
程式碼顯示,僅迭代20次。模型即可達到了標準。在實際執行中,由於選用的圖片,和指定的其它類別不同。迭代次數有可能不同。
程式碼實現:用生成的樣本攻擊模型
接下來就是最有意思的環節了。用訓練好的攻擊樣本來攻擊模型。實現起來非常簡單,直接將input_imgs最為輸入,執行模型。具體程式碼如下:
程式碼12-1 線上性迴歸模型中新增指定節點到檢查點檔案(續)
42 adv = input_imgs.eval() #獲取圖片
43 p = sess.run(probs)[0] #得到模型結果
44 showresult(floatarr_to_img(adv),p) #視覺化模型結果
45 plt.imsave(`dog880.jpg`,floatarr_to_img(adv)) #儲存模型複製程式碼
程式碼執行後,如下結果:
[(880, `傘 `, 0.9981244), (930, `雪糕 冰棍 冰棒 `, 0.00011489283), (630, `脣膏 口紅 `, 8.979097e-05), (553, `女用長圍巾 `, 4.4915465e-05), (615, `和服 `, 3.441378e-05), (729, `塑料袋 `, 3.353129e-05), (569, `裘皮大衣 `, 3.0552055e-05), (904, `假髮 `, 2.2152075e-05), (898, `洗衣機 自動洗衣機 `, 2.1341652e-05), (950, `草莓 `, 2.0412743e-05)]
並得到視覺化圖片,如圖12-4:
圖12-4 模型識別攻擊樣本的結果
從輸出結果和圖12-4的顯示可以看出。模型已經把哈士奇當作了傘。
【程式碼獲取】:關注公眾號:xiangyuejiqiren 公眾號回覆“pycon3”
如果覺得本文有用
可以分享給更多小夥伴