記事ディレクトリ
1. データセット処理
1. データセットをダウンロードする
今回のデータ セットとして、リンゴ、バナナ、ゴレンシの写真をもっと見つけてみてください。
2.統一データセット形式
コードを使用して、画像のサイズとファイル形式を統一します
画像名を統一し、対応する画像の種類に名前 + アンダースコア + 番号を使用します。これは、画像のラベル付けに便利です
# 统一图片格式
fileList = os.listdir(r"C:\Users\cx\Desktop\work\machine_learning\knn\fruit\carambola")
# 输出此文件夹中包含的文件名称
print("修改前:" + str(fileList)[1])
# 得到进程当前工作目录
currentpath = os.getcwd()
# 将当前工作目录修改为待修改文件夹的位置
os.chdir(r"C:\Users\cx\Desktop\work\machine_learning\knn\fruit\carambola")
# 名称变量
num = 1
# 遍历文件夹中所有文件
for fileName in fileList:
# 匹配文件名正则表达式
pat = ".+\ .(jpg|jpeg|JPG)"
# 进行匹配
pattern = re.findall(pat, fileName)
# 文件重新命名
os.rename(fileName, "carambola_" + str(num) + ".jpg")
# fileName.resize(256, 256)
# 改变编号,继续下一项
num = num + 1
print("***************************************")
# 改回程序运行前的工作目录
# os.chdir(currentpath)
# 刷新
sys.stdin.flush()
# 输出修改后文件夹中包含的文件名称
print("修改后:" + str(os.listdir(r"C:\Users\cx\Desktop\work\machine_learning\knn\fruit\carambola"))[1])
統一ファイルサイズ
from PIL import Image
import os
import glob
# 修改图片文件大小
# filename:图片文件名
# outdir:修改后要保存的路径
def convertImgSize(filename, outdir, width=256, height=256):
img = Image.open(filename)
try:
new = img.resize((width, height), Image.BILINEAR)
p = os.path.basename(filename)
print(p)
new.save(os.path.join(outdir, os.path.basename(filename)))
except Exception as e:
print(e)
if __name__ == '__main__':
# 查找给定路径下图片文件,并修改其大小
for filename in glob.glob('C:/Users/cx/Desktop/work/machine_learning/knn/fruit/carambola/*.jpg"):
convertImgSize(filename, 'C:/Users/cx/Desktop/work/machine_learning/knn/fruit/carambola')
最終的に処理された画像:
3. データセットをロードする
データ セットにラベルを追加します: リンゴ、バナナ、ゴレンシ 写真の対応するラベルは次のとおりです: リンゴ、バナナ、ゴレンシ
写真を正規化し、1 次元配列に平坦化します
# 加载数据集
def lode_data():
data = []
labels = []
for img in os.listdir(r"./fruit"):
# 为图片贴标签
label = img.split("_")
labels.append(label[0])
#图片归一化
img = "./fruit/" + img
img = cv2.imread(img, 1)
img = (img - np.min(img)) / (np.max(img) - np.min(img))
data.append(img.flatten())
data = np.array(data)
labels = np.array(labels)
return data, labels
2. トレーニング セットと検証セットの分離
ここでは、カプセル化された方法を直接使用して、上記でロードされた検証セットを 30% の検証セットと 70% の検証セットに分割します。
data, labels = lode_data()
# 从样本中随机抽取30% 做验证集, 其余70% 做训练集
train_data,test_data,train_labels,test_labels = train_test_split(data, labels, test_size = 0.30, random_state = 20, shuffle = True)
3. KNN モデルを定義する
KNN モデル定義にはルーチンがあり、対応する手順に従って適切に実現できます。具体的な手順は次のとおりです。
- ユークリッド距離を計算する
- 計算された距離で並べ替え
- 上位 k 個のサンプル ラベルを取得する
- 最も頻繁に発生するタグを返す
1. ユークリッド距離を計算する
画像を 1 次元ベクトルに展開した後、テスト セット内の各画像と他の画像との間のユークリッド距離を計算できます。各ピクセルに対応して、まず差を計算し、次に二乗と和を計算し、最後に平方根をとってユークリッド距離を求めます。
dis = (np.tile(test_img, (data .shape[0], 1)) - data) ** 2
dis_sq = dis.sum(axis=1)
dis_res = dis_sq ** 0.5
2. すべての距離を並べ替える
argsort() 関数を使用してすべての距離をソートし、対応するインデックスを返します
dis_sort = dis_res.argsort()
3. 最初の k サンプルのラベルを取得する
分類器を構築し、最初の k 個のインデックスを最短距離でトラバースし、インデックスに従って対応するラベルを取得し、最後にすべてのラベルを分類器に入れます。
classcount={
}
for i in range(k):
# 取距离最近的前k个,获取对应标签
vote_label = labels[dis_sort[i]]
classcount[vote_label] = classcount.get(vote_label, 0) + 1
4. 出現回数が最も多いラベルを返す
すべてのタグを降順で並べ替えます。最初のタグは、出現回数が最も多いタグです。
# 将获取的标签进行降序排序
sorted_classcount = sorted(classcount.items(), key = operator.itemgetter(1), reverse = True)
# 返回出现次数最多的标签
return sorted_classcount[0][0]
5. KNN アルゴリズム コード
# knn算法实现
def knn(test_img, data , labels, k):
# 计算欧氏距离
dis = (np.tile(test_img, (data .shape[0], 1)) - data) ** 2
dis_sq = dis.sum(axis=1)
dis_res = dis_sq ** 0.5
# 按照距离依次排序, 返回索引
dis_sort = dis_res.argsort()
# 构造分类器
classcount={
}
for i in range(k):
# 取距离最近的前k个,获取对应标签
vote_label = labels[dis_sort[i]]
classcount[vote_label] = classcount.get(vote_label, 0) + 1
# 将获取的标签进行降序排序
sorted_classcount = sorted(classcount.items(), key = operator.itemgetter(1), reverse = True)
# 返回出现次数最多的标签
return sorted_classcount[0][0]
4. テストモデル
上記の KNN モデルの定義では、テスト データを入力すると、距離 K で最も多く出現するラベルが返されます。テスト セット内の各テスト サンプルを使用して、モデルによって返されたラベルを独自の正しいラベルと比較し、最終的に正しい率を取得します。K の値は 0 から 20 までトラバースし、各 K 値に対応する正しいレートが出力されます。
# 获取标签匹配成功的概率
def test_all(train_data, train_labels, test_data, test_labels, k):
right = 0
for i in range(len(test_data)):
if knn(test_data[i], train_data, train_labels, k) == test_labels[i]:
right+=1
return right/len(test_data)
# 训练
def train():
right = []
data, labels = lode_data()
# 从样本中随机抽取20% 做验证集, 其余80% 做训练集
for k in range(0, (len(labels)-1)):
train_data,test_data,train_labels,test_labels = train_test_split(data, labels, test_size = 0.20
,random_state = 20, shuffle = True)
right.append(test_all(train_data, train_labels, test_data, test_labels, k + 1))
i = str(k + 1)
print('K = {}, 正确率 = {}'.format(i, right[k]))
plt.plot( range(len(test_data) + 1) , right)
plt.show()
train()
1. K値と精度曲線
2. 結果分析
いくつかの代表的な k 値分析を行います。
-
K = 5 の場合、トレーニングの正解率はわずか 0.59 です.この結果は、2 つの写真の背景が単純すぎること、空白の場所が多いこと、および 2 種類の果物の形とサイズが似ていることが原因である可能性があります. . これらの空白の場所のグレー値は非常に似ており、異なる果物によって計算されたユークリッド距離は非常に小さいです。K 値が比較的小さい場合、トレーニング セット内の 2 つの画像は一意であり、満足のいく結果が得られません。たとえば、次の 2 つの写真では、フレームはほぼ写真のサイズであり、同じグレースケールの空白領域が多数あります. 次の 2 つの異なる果物は、比較的小さい距離を持っています:
-
K = 40 の場合、正しいレートは 0.72 です。私の 3 つの果物のサンプル セットの数は正確には等しくなく、約 50 個のリンゴ、40 個のゴレンシ、および 60 個のバナナです。最後に、k の値が大きすぎると、距離 k のテスト結果では、サンプル数が多いラベルほどカウントされやすくなります。さらに、いくつかの写真の背景は比較的ぼやけており、2つの写真の違いは比較的大きく、最終的に、同じ果物に対して計算された距離は比較的大きく、一致する確率は比較的小さくなります。たとえば、次の同じ種類の果物間の距離は比較的大きくなります。
-
K = 30 の場合、正しいレートは 0.81 です。3 つのデータセットの全体的な違いは比較的明白であり、k 値が比較的小さい場合に出現する異なる果物によって計算されるユークリッド距離は小さく、同じ果物によって計算されるユークリッド距離は比較的大きいという状況であるため、最終テスト結果は理想的です。さまざまな果物の全体的な違いは明らかです。