# 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: 圖一
>
>
:point_down: 圖二
>
>
:point_down: 圖三
>
>
:point_down: 圖四
>
>
:point_down:全圖
>
## :memo:結果討論
首先我用小畫家選取鴨子和非鴨子查看色碼並記錄起來(如上述數據表格)
鴨子圖片的部分則是轉成RGB以array形式存取再進行分類
因原先的圖片過於龐大,因此我先切部分圖片作為測試(圖一至圖四)
再使用原圖片來訓練,否則單次測試時間會過於冗長
由上述執行結果圖片來看辨識度不錯,白色部分為鴨子,黑色部分為非鴨子
這次的作業讓我對貝氏分類更為熟悉對於應用的層面更廣泛、實用性也更高
整體來說是份蠻有趣的作業