# Finding Ducks 資工四 410711225 連昱婷 ## :memo:方法及步驟 我是使用之前機器學習的「貝氏定理辨識鳶尾花」為基礎來做這次的作業,這次一樣是使用**Anaconda**的環境來做。 測試資料集我是利用小畫家匯入圖片,根據鴨子和非鴨子取色出RGB紀錄於csv檔,再從csv檔匯入檔案做訓練模型。數據包含鴨子與非鴨子共30筆。 貝氏定理: 概率論中的一個定理,描述在已知一些條件下某事件的發生機率。 公式 ***P(A|B) = (P(A)P(B|A)) / P(B)*** >其中 A 以及 B 為隨機事件,且 P(B)不為零。P(A|B)是指在事件 B 發生的情況下事件 A 發生的概率。 P( A | B ):已知 B 發生後,A 的條件機率,被稱作 A 的後驗概率 P( B | A ) :已知 A 發生後,B 的條件機率 P(A):A 的先驗概率(不考慮任何 B 方面因素) P(B):B 的先驗概率(不考慮任何 A 方面因素) 1. 處理數據 載入圖片轉換成像素數據做處理 1. 提取特徵結果 提取訓練數據之屬性特徵,以便做計算並預測結果 1. 預測結果: 擷取部分圖片作為測試用預測模型的訓練結果 1. 結果輸出: 將結果以圖片輸出評估模組的準確度 ## :memo:數據 label:鴨子(1)/非鴨子(2) | R | G | B | label | | ---- | ---- | ---- | --- | |231|246|253|1 |233|250|255|1 |234|248|251|1 |236|251|255|1 |238| 250| 250| 1 |240| 249| 255| 1 |233| 233| 245| 1 |241| 241| 249| 1 |243| 251| 254| 1 |242| 250| 253| 1 |246| 251| 255| 1 |236| 247| 253| 1 |242| 247| 253| 1 |239| 244| 248| 1 |239| 250| 255| 1 |208| 230| 244| 1 |248| 252| 255| 1 |239| 247| 250| 1 |244| 251| 255| 1 |236| 243| 251| 1 |40| 60| 51| 2 |150| 150| 152| 2 |86| 96| 106| 2 |87| 142| 158| 2 |57| 90| 35| 2 |96| 95| 109| 2 |108| 99| 92| 2 |135| 142| 148| 2 ## :memo:程式碼 ```python= import csv import cv2 import numpy as np from PIL import Image import pandas as pd import math def loadcsv(file): data = csv.reader(open(file, "r")) #文件以讀取(r)方式打開 dataset = list(data) #每行存入list for i in range(1, len(dataset)): #將標題過濾所以range從1開始 dataset[i] = [float(x) for x in dataset[i]] #轉成數字的data存入dataset return dataset #將圖片轉為RGB表示 def loadduck(file): img = Image.open(file) pixels = img.convert("RGB") indices_arr = np.moveaxis(np.indices(img.size), 0, 2) color_arr = np.array(pixels.getdata()).reshape(img.size + (3,)) arr = np.dstack((indices_arr, color_arr)).reshape((-1, 5)) #轉dataframe df = pd.DataFrame(arr, columns=["x", "y", "red", "green", "blue"]) df.drop(['x', 'y'], axis=1, inplace=True) data = df.to_numpy().astype(float).tolist() return data #標準差 def SD(num): avg = sum(num) / float(len(num)) variance = sum([pow(x - avg, 2) for x in num]) / float(len(num) - 1) sd = math.sqrt(variance) return sd #計算每個屬性的平均值和標準差 def summarize(dataset): summaries = [(sum(attribute) / float(len(attribute)), SD(attribute)) for attribute in zip(*dataset[1:-2])] #因serial id和label不需要 #zip->資料樣本可以照屬性分組為一個個清單,然後對每個屬性計算平均值、標準差 del summaries[-1] return summaries #將每個lable分類,分別存入seperated #最後一個屬性-1為類別值,返回一個類別值到資料樣本清單的映射 def class_separate(dataset): separate = {} for i in range(len(dataset)): vector = dataset[i] if (vector[-1] not in separate): #如果vector[-1](最後一欄即label)沒有在seperate裡的話,加入 separate[vector[-1]] = [] separate[vector[-1]].append(vector) return separate #將訓練資料集按照類別label進行劃分,計算每個屬性的特徵 def class_summarize(dataset): separate = class_separate(dataset) summaries = {} for class_value, instances in separate.items(): summaries[class_value] = summarize(instances) return summaries #高斯概率密度函數 def probability(x, mean, SD): exponent = math.exp(-(math.pow(x - mean, 2) / (2 * math.pow(SD, 2)))) prob = (1 / (math.sqrt(2 * math.pi) * SD)) * exponent return prob #給定一個資料樣本,它所屬每個類別的概率,可以通過將其屬性概率相乘得到,結果是一個類值到概率的映射 def class_probability(summaries, inputv): prob = {} for class_value, class_summ in summaries.items(): prob[class_value] = 1 for i in range(len(class_summ)): mean, SD = class_summ[i] x = inputv[i] prob[class_value] *= probability(x, mean, SD) return prob #計算一個資料樣本屬於每個類的概率,找到最大的概率值,並返回關聯的類別 def predict(summaries, inputv): prob = class_probability(summaries, inputv) best_label, best_prob = None, -1 for class_value, probability in prob.items(): if best_label is None or best_prob < probability: best_prob = probability best_label = class_value return best_label #透過對測試資料集中每個資料樣本的預測,可以評估模型精準度 def get_predict(summaries, testSet): prediction = [] for i in range(len(testSet)): result = predict(summaries, testSet[i]) prediction.append(result) return prediction #建立新的圖片結果 def create(prediction,imgpath): imgs = [] for i in range(len(prediction)): #判斷是否為鴨子,是的話印出白色,否則黑色 if(int(prediction[i]) == 1): imgs.append([255,255,255]) else: imgs.append([0,0,0]) img = cv2.imread(imgpath) size = img.shape imgs = np.array(imgs) array = np.reshape(imgs, (size[0], -1)) new = Image.fromarray(array) new = new.resize((392, 393)) new.show() print("OK") def main(): imgpath = "4.jpg" #輸入要辨識的圖片檔 train = loadcsv("train.csv") #輸入data train = train[1:] #將標題過濾 test = loadduck(imgpath) #將圖片轉為RGB表示 summ = class_summarize(train) #將按照label進行劃分的資料集存入summ pred = get_predict(summ, test) #預測 create(pred,imgpath) #建立出圖片結果 main() ``` ## :memo:執行成果 :point_down: 圖一 >![](https://i.imgur.com/IyefZYi.jpg =60%x) >![](https://i.imgur.com/LjCEO7r.png =60%x) :point_down: 圖二 >![](https://i.imgur.com/0QgRfka.jpg =60%x) >![](https://i.imgur.com/iLtz5uF.png =60%x) :point_down: 圖三 >![](https://i.imgur.com/3gAUhwP.jpg =60%x) >![](https://i.imgur.com/5Ef9SUQ.png =60%x) :point_down: 圖四 >![](https://i.imgur.com/KXv279T.jpg =60%x) >![](https://i.imgur.com/mURo61B.png =60%x) :point_down:全圖 >![](https://i.imgur.com/0goHJAQ.png) ## :memo:結果討論 首先我用小畫家選取鴨子和非鴨子查看色碼並記錄起來(如上述數據表格) 鴨子圖片的部分則是轉成RGB以array形式存取再進行分類 因原先的圖片過於龐大,因此我先切部分圖片作為測試(圖一至圖四) 再使用原圖片來訓練,否則單次測試時間會過於冗長 由上述執行結果圖片來看辨識度不錯,白色部分為鴨子,黑色部分為非鴨子 這次的作業讓我對貝氏分類更為熟悉對於應用的層面更廣泛、實用性也更高 整體來說是份蠻有趣的作業