本文情境參考:http://joelgrus.com/2016/05/23/fizz-buzz-in-tensorflow/ 我特別找到了這個範例(?)來說服大家,為什麼我們的智慧音箱課程不實作語音或者文字類的機器學習。 這個故事大致上是這樣的:fizzbuzz是一個歐美國家的小遊戲,大家輪流數數字,當遇到3的倍數時則將數字改為念出fizz,遇到5的倍數時則改念buzz,遇到15的倍數時念fizzbuzz。這個遊戲在台灣我記得也有另外一個版本,但大致上就是這樣。 後來他變成一個程式設計的練習題,用來熟悉for、if以及數學運算,其要求的輸出如下: ```python [‘1’, ‘2’, ‘fizz’, ‘4’, ‘buzz’, ‘fizz’, ‘7’, ‘8’, ‘fizz’, ‘buzz’, ’11’, ‘fizz’, ’13’, ’14’, ‘fizzbuzz’, ’16’, ’17’, ‘fizz’, ’19’, ‘buzz’, ‘fizz’, ’22’, ’23’, ‘fizz’, ‘buzz’, ’26’, ‘fizz’, ’28’, ’29’, ‘fizzbuzz’, ’31’, ’32’, ‘fizz’, ’34’, ‘buzz’, ‘fizz’, ’37’, ’38’, ‘fizz’, ‘buzz’, ’41’, ‘fizz’, ’43’, ’44’, ‘fizzbuzz’, ’46’, ’47’, ‘fizz’, ’49’, ‘buzz’, ‘fizz’, ’52’, ’53’, ‘fizz’, ‘buzz’, ’56’, ‘fizz’, ’58’, ’59’, ‘fizzbuzz’, ’61’, ’62’, ‘fizz’, ’64’, ‘buzz’, ‘fizz’, ’67’, ’68’, ‘fizz’, ‘buzz’, ’71’, ‘fizz’, ’73’, ’74’, ‘fizzbuzz’, ’76’, ’77’, ‘fizz’, ’79’, ‘buzz’, ‘fizz’, ’82’, ’83’, ‘fizz’, ‘buzz’, ’86’, ‘fizz’, ’88’, ’89’, ‘fizzbuzz’, ’91’, ’92’, ‘fizz’, ’94’, ‘buzz’, ‘fizz’, ’97’, ’98’, ‘fizz’, ‘buzz’] ``` 於是乎,有些公司也會拿來當成程式設計的面試題目,來檢查考生對程式語言的基本功及理解。才有了底下這個(不知道是不是真的)的故事: 某個工程師……我們暫且稱呼他A君,到某家有名的科技公司應徵軟體工程師,而面試官就恰恰好考了這個問題。A君覺得有名科技公司還考這種題目簡直瞧不起人,於是用了機器學習繞了一大圈,揮舞了一整套路的牛刀砍死了一隻雞。 在開始接下來的「故事」之前,身為一個好的python使用者,先引用好我們等等會碰到的東西: ```python import numpy as np from keras.models import Sequential from keras.layers import Dense ``` 首先,我們知道機器學習--這種狀況應是分類問題,需要三個重點:特徵、分類對象、類別 對數字來說特徵可以定義成他的二進制表示--這省去了我們標準化的動作,且可以輕鬆的轉換出來。由於要求是1~100的fizzbuzz,我們就使用101到……4095作為訓練資料吧,剛好是12碼的二進碼: ```python #定義編碼長度 NUM_DIGITS = 12 ``` 首先我們寫一個函式來轉換二進制。這邊的二進碼必須一碼當一項特徵資料,以利我們後續直接送進神經網路: ```python #轉成二進制數字 def binary_encode(i, num_digits): return np.array([i >> d & 1 for d in range(num_digits)]) ``` 接著我們知道,分類對象就是1~4095這些數字,於是我們剩下定義類別。類別可以看出是四類:fizz、bizz、fizzbuzz以及以上皆非。四類問題我們直接使用onehot編碼,可以直接對應到神經網路的輸出層。 接著來準備一下訓練資料,訓練資料為101~4095的數字: ```python #訓練資料,生成12碼二進制數字(101~4095) train_data = np.array([binary_encode(i, NUM_DIGITS) for i in range(101, 2 ** NUM_DIGITS)]) ``` 有了資料,接著要為訓練資料標上標籤給機器學習,我們先定義一個拿到標籤的函式: ```python #把輸出變成一次只激活一個細胞的標籤 def fizz_buzz_encode(i): if i % 15 == 0: return np.array([0, 0, 0, 1]) elif i % 5 == 0: return np.array([0, 0, 1, 0]) elif i % 3 == 0: return np.array([0, 1, 0, 0]) else: return np.array([1, 0, 0, 0]) ``` 呼叫他,建立訓練資料標籤的陣列: ```python #資料標籤 train_ans = np.array([fizz_buzz_encode(i) for i in range(101, 2 ** NUM_DIGITS)]) ``` 接著就來建我們的神經網路啦,因為我們比較菜,所以使用keras來一層一層建: ```python #快樂的建立神經層吧 model = Sequential() #輸入層,一顆對應一個編碼 model.add(Dense(NUM_DIGITS,input_shape =(NUM_DIGITS,))) #隱藏層 model.add(Dense(200, activation=’relu’)) #輸出層,因為輸出是4類所以塞4顆,如果想製造自己的麻煩也可以用3顆去表達4種狀態…… model.add(Dense(4, activation=’relu’)) #看看我們的簡單網路堆疊成什麼樣子 model.summary() #指定損失函數 model.compile(loss=’mean_squared_error’, optimizer=’adam’, metrics=[‘accuracy’]) ``` 很快樂的建立完了,詳細的參數請看內部註解。接著就是訓練啦: ```python #訓練的時間到了 train_history = model.fit(x=train_data,y=train_ans, validation_split=0.2,epochs=200, batch_size=50, verbose=2) ``` 好了,到這裡我們有了一個不錯的神經網路以及訓練完的模型,最後就是丟真實資料1~100給它分類看看,記得要把輸出改回fizzbuzz: ```python #轉回fizzbuzz輸出 label = {0:’none’,1:’fizz’,2:’buzz’,3:’fizzbuzz’} prediction = model.predict_classes(np.array([binary_encode(i, NUM_DIGITS) for i in range(1,101)])) index = 1 for i in prediction: print(index,label[i]) index += 1 ``` 正確率實行結果大概9成9,算是相當了不起的正確率,到這裡我們相信我們有了一個好方法來解決這個問題……才怪= = 我們仔細回想一下,我們前面在生訓練資料時拿標籤的方法: ```python def fizz_buzz_encode(i): if i % 15 == 0: return np.array([0, 0, 0, 1]) elif i % 5 == 0: return np.array([0, 0, 1, 0]) elif i % 3 == 0: return np.array([0, 1, 0, 0]) else: return np.array([1, 0, 0, 0]) ``` ……直接把這邊丟1~100輸出不就好了嗎 所以說,很多其實一眼可以看穿規則的東西,丟給機器學習,真的沒有必要--比方說我們研習教學的文字分析系統。 順便一提面試的正解應該是以下這樣: ```python print([‘fizzbuzz’ if i%15==0 else ‘buzz’ if i%5 == 0 else ‘fizz’ if i%3 == 0 else str(i) for i in range(1,101)]) ``` 對,一行(O)