เนื่องจากอยากได้ข้อมูลรูปภาพใหม่ๆมาเพื่อทดสอบเทคนิคการเรียนรู้ของเครื่องในการวิเคราะห์รูปภาพเลยได้ลองคิดหาวิธีสำหรับสร้างชุดข้อมูลใหม่ขึ้นมา
ชุดข้อมูลสามารถหาได้มากมายจากในอินเทอร์เน็ต แต่ถ้าหากสามารถสร้างและปรับแต่งอะไรเองได้ก็คงจะสะดวกไม่น้อย
การสร้างอาจทำง่ายๆโดยใช้แค่ numpy และ matplotlib
เกี่ยวกับวิธีการวาดรูปด้วย matplotlib อ่านได้ใน
https://phyblas.hinaboshi.com/numa36 ข้อมูลเป็นรูปร่างต่างๆ ๕ ชนิด ได้แก่
- วงรี
- สามเหลี่ยม
- สี่เหลี่ยม
- ดาว
- กากบาท
และยังมีแบบว่างเปล่าไม่มีรูปอะไรอยู่เลยด้วย รวมทั้งหมดเป็น ๖ กลุ่ม
สามารถกำหนดขนาดรูปที่ต้องการได้เอง
รูปตัวอย่างขนาด 40×40
โค้ดสำหรับสร้าง
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import os
def sangrup(n,d=25,misi=1,langlai=1,aowang=1,sakun='png',f='ruprang',yaek=1):
'''n: จำนวนรูปที่จะสร้างต่อชนิด
d: ขนาดรูป
misi: มีสีหรือไม่
langlai: พื้นหลังมีลายหรือไม่
aowang: เอาภาพพื้นว่างๆด้วยหรือไม่
sakun: สกุลภาพ
f: โฟลเดอร์เก็บภาพ
yaek: แยกรูปแต่ละประเภทไว้คนละโฟลเดอร์
'''
c = 5+aowang # จำนวนชนิด
# สร้างโฟลเดอร์ใหม่ ถ้ายังไม่มีอยู่
if(not os.path.exists(f)):
os.mkdir(f)
if(yaek):
for i in range(c):
fyoi = os.path.join(f,'%d'%i)
if(not os.path.exists(fyoi)):
os.mkdir(fyoi)
lw = np.random.uniform(1,4,[n,5]) # ความหนาเส้นขอบ
if(misi):
si1,si2 = np.random.uniform(0,1,[2,n,5,3]) # สีผิว, สีเส้นขอบ
if(langlai):
phuen = np.random.normal(0,1,[n,d,d,3]).cumsum(1) # สีพื้นหลัง
phuen -= phuen.min(1)[:,None]
phuen /= phuen.max(1)[:,None]
phuen[::2] = phuen[::2].transpose(0,2,1,3)
phuen *= np.linspace(0,1,n)[:,None,None,None]
phuen += np.random.uniform(0,np.linspace(1,0,n),[3,n]).T[:,None,None,:]
else:
phuen = np.tile(np.random.uniform(0,1,[n,3]),[d,d,1,1]).transpose(2,0,1,3)
else:
si1,si2 = np.ones([n,5,3]),np.zeros([n,5,3])
aowang = 0
from PIL import Image
d_2 = (d-1)/2. # จุดกึ่งกลาง
# วงรี
k,s = np.random.uniform(0.4,0.9,[2,n])*d # กว้าง, สูง
mum = np.random.uniform(0,180,n) # มุมเอียง
# ฟังก์ชันสร้างจุด
def sangchut(r,theta):
x = r*np.cos(np.radians(theta))*d+d_2
y = r*np.sin(np.radians(theta))*d+d_2
return np.stack([x,y],2)
# สามเหลี่ยม
theta = np.random.uniform(0,120,[n,1]) + np.random.normal(np.linspace(0,240,3),15,[n,3])
r = np.random.uniform(0.25,0.45,[n,3])
X3 = sangchut(r,theta)
# สี่เหลี่ยม
theta = np.random.uniform(0,90,[n,1]) + np.random.normal(np.linspace(0,270,4),15,[n,4])
r = np.random.uniform(0.25,0.45,[n,4])
X4 = sangchut(r,theta)
# ดาว
theta = np.random.uniform(0,36,[n,1]) + np.random.normal(np.linspace(0,324,10),6,[n,10])
r = np.random.uniform(0.35,0.45,[n,10])*(np.arange(10)%2*0.5+0.5)[None,:]
Xd = sangchut(r,theta)
# กากบาท
a = np.random.uniform(10,25,n)
theta = (np.random.uniform(0,360,[n,1])+np.arange(0,360,90).repeat(3)) + np.tile(np.array([-a,a,45*np.ones(n)]).T,4)
r = np.tile(np.random.uniform(0.35,0.45,[n,1])*np.hstack([np.ones([n,2]),np.random.uniform(0.15,0.35,[n,1])]),4)
Xk = sangchut(r,theta)
for i in range(n):
for j in range(c):
plt.figure(figsize=[1,1])
ax = plt.axes([0,0,1,1],aspect=1,xlim=[-0.5,d-0.5],ylim=[-0.5,d-0.5])
if(misi):
ax.imshow(phuen[i])
if(j==0):
ax.add_patch(mpl.patches.Ellipse([d_2,d_2],k[i],s[i],angle=mum[i],fc=si1[i,j],ec=si2[i,j],lw=lw[i,j]))
elif(j==1):
ax.add_patch(plt.Polygon(X3[i],fc=si1[i,j],ec=si2[i,j],lw=lw[i,j]))
elif(j==2):
ax.add_patch(plt.Polygon(X4[i],fc=si1[i,j],ec=si2[i,j],lw=lw[i,j]))
elif(j==3):
ax.add_patch(plt.Polygon(Xd[i],fc=si1[i,j],ec=si2[i,j],lw=lw[i,j]))
elif(j==4):
ax.add_patch(plt.Polygon(Xk[i],fc=si1[i,j],ec=si2[i,j],lw=lw[i,j]))
plt.axis('off')
if(yaek):
chuefile = os.path.join(f,'%d/%05d.%s'%(j,i+1,sakun))
else:
chuefile = os.path.join(f,'%06d.%s'%(i+1+j*100000,sakun))
plt.savefig(chuefile,dpi=d)
plt.close()
if(not misi):
Image.open(chuefile).convert('L').save(chuefile)
print('สร้างเสร็จแล้ว')
ภาพจะถูกเซฟไว้ แล้วเวลาจะอ่านรูปก็ใช้ glob ค้นทั้งโฟลเดอร์แล้ว imread เพื่ออ่าน เช่นเขียนแบบนี้
from glob import glob
X = np.array([plt.imread(x) for x in sorted(glob(os.path.join(f,'*','*.'+sakun)))])
z = np.arange(5+aowang*misi).repeat(n)
อาจสร้างฟังก์ชันสำหรับดึงภาพได้ในลักษณะแบบนี้
def anrup(sangmai,n,d=25,misi=1,langlai=1,aowang=1,sakun='png',f='ruprang',yaek=1):
if(sangmai):
sangrup(n,d,misi,langlai,aowang,sakun,f,yaek)
if(yaek):
chuefile = os.path.join(f,'*','*.'+sakun)
else:
chuefile = os.path.join(f,'*.'+sakun)
X = np.array([plt.imread(x) for x in sorted(glob(chuefile))])
if(misi):
X = X[:,:,:,:3]
z = np.arange(5+aowang*misi).repeat(n)
return X,z
มีตัวเลือกให้สามารถปรับเลือกแบบไม่มีสี หรือไม่ใส่ลายหลัง หรือจะไม่เอาลายที่ไม่มีรูปอะไรมีแค่พื้นหลังเปล่าๆก็ได้
แบบไร้สี
แบบมีสีแต่ไม่ใส่ลายหลัง
ต่อมาลองทดสอบการใช้งานกันดู
เทคนิคที่จะใช้คือวิธีการเพื่อนบ้านใกล้สุด k ตัว ซึ่งเคยพบว่าใช้ได้ผลดีมากกับข้อมูล MNIST
(รายละเอียด
https://phyblas.hinaboshi.com/20171028)
เพื่อความง่ายขอใช้แค่แบบไร้สี ขนาด 25×25 ถ้าเป็นแบบมีสีและหลังลายจะไม่มีทางใช้วิธีการนี้ทายได้เลยเพราะมีความซับซ้อน ต้องใช้โครงข่ายประสาทเทียมแบบคอนโวลูชัน
การทดสอบจะทำด้วยการตรวจสอบแบบไขว้ k-fold (รายละเอียด
https://phyblas.hinaboshi.com/20171018) ทำการหาเส้นโค้งการเรียนรู้ เพื่อเปรียบเทียบว่าเมื่อใช้ข้อมูลเรียนรู้จำนวนมากแค่ไหนจะสามารถเรียนรู้จนทายได้ถูกต้องแค่ไหน (รายละเอียด
https://phyblas.hinaboshi.com/20171024)
from sklearn.neighbors import KNeighborsClassifier as Knn
from sklearn.model_selection import StratifiedKFold
import time
d = 25 # ขนาด
n = 5000 # จำนวนรูปแต่ละชนิด
X,z = anrup(1,n,d,misi=0) # อ่านรูปขึ้นมา ถ้าหากได้สร้างรูปเตรียมไว้แล้วให้ใส่เป็น anrup(0,n,d,misi=0) จะไม่ต้องสร้างใหม่
X = X.reshape(-1,d*d)
sumriang = np.random.permutation(len(z))
X,z = X[sumriang],z[sumriang]
maen_fuek = [],[],[],[],[]
maen_truat = [],[],[],[],[]
nknk = np.arange(2000,20001,2000) # จำนวนข้อมูลที่ใช้ฝึกในแต่ละรอบ
skf = StratifiedKFold(n_splits=5,shuffle=True) # ทำ ๕ ครั้ง หาค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐาน
t1 = time.time()
for i,(fuek,truat) in enumerate(skf.split(X,z)):
X_fuek,X_truat,z_fuek,z_truat = X[fuek],X[truat],z[fuek],z[truat]
for nk in nknk:
knn = Knn(n_neighbors=3,n_jobs=-1) # ใช้เพื่อนบ้านใกล้สุด ๓ ตัว
knn.fit(X_fuek[:nk],z_fuek[:nk])
maen_fuek[i].append(knn.score(X_fuek[:200],z_fuek[:200])) # ความแม่นของข้อมูลฝึก
maen_truat[i].append(knn.score(X_truat[:200],z_truat[:200])) # ความแม่นของข้อมูลตรวจสอบ
print(u'ใช้ %d รอบ %d เวลาผ่านไปแล้ว %.2f วินาที แม่น %.2f'%(nk,i+1,time.time()-t1,maen_truat[i][-1]))
mf = np.array(maen_fuek)
mt = np.array(maen_truat)
plt.errorbar(nknk,mf.mean(0),yerr=mf.std(0),color='#BB5577',capsize=2)
plt.errorbar(nknk,mt.mean(0),yerr=mt.std(0),color='#339955',capsize=2)
plt.xlabel(u'จำนวนข้อมูลฝึก',family='Tahoma')
plt.ylabel(u'ความแม่นยำ',family='Tahoma')
plt.legend([u'ฝึกฝน',u'ตรวจสอบ'],prop={'family':'Tahoma'})
plt.show()
ผลเป็นดังนี้
เมื่อใช้ตัวอย่างสำหรับฝึกเยอะพอ ใช้แค่วิธีการง่ายๆแบบนี้ก็สามารถทำนายได้แม่นยำว่า 95%
ก็หวังว่าจะมีคนเอาไปลองใช้กันดู