ช่วงนี้กำลังพยายามเขียนเนื้อหาเรื่องโครงข่ายประสาทเทียมแบบคอนโวลูชันอยู่ จึงได้พยายามสร้างภาพต่างๆขึ้นมาอธิบาย ซึ่งก็โชคดีที่ได้มารู้จักมอดูลที่ใช้งานได้ดีตัวหนึ่ง นั่นคือ
manim (คลิกลิงก์เพื่อดูเนื้อหาสอนการใช้งาน manim ได้)
การคำนวณที่เกิดขึ้นในชั้นคอนโวลูชันนั้นเป็นอะไรที่เข้าใจยาก มองเห็นภาพยากอยู่ การวาดภาพดีๆจะช่วยให้เข้าใจได้ง่ายขึ้น โดยเฉพาะถ้าทำเป็นภาพเคลื่อนไหวได้น่าจะช่วยอธิบายได้ดี
ก่อนหน้านี้ได้เริ่มทำการเขียนเนื้อหา
โครงข่ายประสาทเทียมเบื้องต้น ลงไว้ในบล็อก แต่ก็ค้างอยู่ตรงบทที่ ๒๐ เรื่องของโครงข่ายประสาทเทียมแบบคอนโวลูชัน เนื่องจากคิดหาวิธีในการเขียนอธิบายให้เข้าใจง่ายๆไม่ได้
งานนี้เลยค้างมาตั้งแต่ปี 2018 เคยคิดจะเขียนต่อ แต่ก็ไม่ได้เขียนสักที จนเวลาก็ผ่านมาถึง ๒ ปีครึ่งแล้ว
พอได้รู้จัก manim เลยคิดว่าเป็นโอกาสที่ดีที่จะนำมาใช้ในการอธิบายโครงค่ายประสาทเทียมแบบคอนโวลูชัน จึงสามารถเขียน
บทที่ ๒๐ ออกมาได้ในที่สุด
จากนั้นจึงได้ฝึก manim จนเริ่มใช้งานได้คล่อง แล้วก็ได้สร้างวีดีโอตัวนี้ขึ้นมา ลงไว้ใน facebook >>
https://www.facebook.com/watch/?v=232382701949345แต่วีดีโอนี้ไม่ได้มีอะไรเพิ่มเติมไปจากเดิมที่เคยทำโดยเขียนด้วย matplotlib ซึ่งเคยใช้ลงไว้ในใน
บทความที่อธิบายเรื่องคอนโวลูชันที่จริงแล้วการคำนวณที่เกิดขึ้นจริงภายในโครงข่ายประสาทเทียมนั้นยิ่งซับซ้อนกว่าในรูปนั้นไปอีก เพราะประกอบด้วยตัวกรองหลายตัวซึ่งต้องมาจับคู่คูณกับค่าป้อนเข้าหลายตัว และยังมีการบวกด้วยไบแอสด้วย ถึงจะคำนวณออกมาเป็นผลลัพธ์ในแต่ละชั้น
ดังนั้นต่อมาจึงได้สร้างภาพนี้ขึ้นมาเพื่ออธิบายคือภาพนี้ >>
https://www.facebook.com/watch/?v=904503580382726ในภาพนี้มี
- จำนวนช่องขาเข้า 3
- จำนวนช่องขาออก 3
- ขนาดตัวกรองเป็น 3×3
- ขนาดข้อมูลขาเข้าเป็น 4×4
- ดังนั้นผลลัพธ์ที่ได้ก็จะเป็นขนาด 2×2
- พารามิเตอร์น้ำหนักบนตัวกรองมีทั้งหมด 3×3×3×3=27 ตัว
- บวกกับพารามิเตอร์ไบแอสอีก 3 ตัว (เท่ากับจำนวนช่องขาออก)
สำหรับกรอบต่างๆที่เห็นในรูปนี้
- สีเขียวคือค่าป้อนเข้า (input)
- สีน้ำเงินคือตัวกรอง (kernel)
- สีม่วงคือไบแอส (bias)
- สีแดงคือผลลัพธ์ (output)
ต่อไปนี้เป็นโค้ดที่ใช้สร้างวีดีโอนั้นขึ้น
import numpy as np
import manimlib as mnm
class Manimala(mnm.Scene):
def construct(self):
nx = [3,4,4]
nk = [3,3,3,3]
nz = [nk[0],nk[1],nx[1]-nk[2]+1,nx[2]-nk[3]+1]
x = np.random.randint(0,10,nx)
k = np.random.randint(0,10,nk)
b = np.empty(nk[0])
zh = np.empty(nz)
zh_ruam = np.empty([nz[0],nz[1]+1,nz[2],nz[3]])
zk = np.empty([nz[0],nz[2],nz[3],nz[1],nk[2],nk[3]])
for g in range(nz[0]):
for h in range(nz[1]):
for j in range(nz[2]):
for i in range(nz[3]):
z_ghji = (x[h,j:j+nk[2],i:i+nk[3]]*k[g,h])
zk[g,j,i,h] = z_ghji
zh[g,h,j,i] = z_ghji.sum()
b[g] = np.random.randint(10,100)
zh_ruam[g,0] = b[g]
zh_ruam[g,1:] = zh[g].cumsum(0)
lek_x = []
krop_x = []
for h in range(nx[0]):
lek_x_h = []
krop_x_h = []
for j in range(nx[1]):
for i in range(nx[2]):
lek_x_hji = mnm.Tex('%d'%x[h,j,i],font_size=60)
tamnaeng = [-1.5+i+h*0.5,-j+h*1.5,0]
lek_x_hji.move_to(tamnaeng)
lek_x_h.append(lek_x_hji)
krop_x_hji = mnm.Square(1,fill_opacity=1,fill_color='#466422',color='#2c3711')
krop_x_hji.move_to(tamnaeng)
krop_x_h.append(krop_x_hji)
lek_x.append(mnm.VGroup(*lek_x_h))
krop_x.append(mnm.VGroup(*krop_x_h))
lek_x = mnm.VGroup(*lek_x)
krop_x = mnm.VGroup(*krop_x)
lek_k = []
krop_k = []
for g in range(nk[0]):
lek_k_g = []
krop_k_g = []
for h in range(nk[1]):
lek_k_gh = []
krop_k_gh = []
for j in range(nk[2]):
for i in range(nk[3]):
lek_k_ghji = mnm.Tex('%d\\times'%k[g,h,j,i],color='#d6e9a8',font_size=36)
tamnaeng = [-1.9+i+h*0.5-4.5+g*0.5/3,0.1-j+h*1.5-1+g*0.5,0]
lek_k_ghji.move_to(tamnaeng)
lek_k_gh.append(lek_k_ghji)
krop_k_ghji = mnm.Square(1,fill_opacity=1,fill_color='#277ad7',color='#101240',stroke_opacity=0.3)
krop_k_ghji.move_to(tamnaeng)
krop_k_gh.append(krop_k_ghji)
lek_k_g.append(mnm.VGroup(*lek_k_gh))
krop_k_g.append(mnm.VGroup(*krop_k_gh))
lek_k.append(mnm.VGroup(*lek_k_g))
krop_k.append(mnm.VGroup(*krop_k_g))
lek_k = mnm.VGroup(*lek_k)
krop_k = mnm.VGroup(*krop_k)
lek_zk = [[[],[],[]],[[],[],[]],[[],[],[]]]
krop_zk = [[[],[],[]],[[],[],[]],[[],[],[]]]
for g in range(nz[0]):
for jz in range(nz[2]):
for iz in range(nz[3]):
for h in range(nz[1]):
lek_zk_gh = []
krop_zk_gh = []
for jk in range(nk[2]):
for ik in range(nk[3]):
tex = '%d'%zk[g,jz,iz,h,jk,ik]
lek_zk_ghji = mnm.Tex(tex,color='#d59ee7',stroke_opacity=1,stroke_width=2,font_size=80)
tamnaeng = [-1.9+ik+iz+h*0.5,0.1-jk-jz+h*1.5,0]
lek_zk_ghji.move_to(tamnaeng)
lek_zk_gh.append(lek_zk_ghji)
krop_zk_ghji = mnm.Square(1,fill_opacity=0.8,fill_color='#277ad7',color='#101240',stroke_opacity=0.3)
krop_zk_ghji.move_to(tamnaeng)
krop_zk_gh.append(krop_zk_ghji)
lek_zk[g][h].append(mnm.VGroup(*lek_zk_gh))
krop_zk[g][h].append(mnm.VGroup(*krop_zk_gh))
lek_z = []
krop_z = []
lek_b = []
krop_b = []
for g in range(nz[0]):
lek_z_g = []
krop_z_g = []
for h in range(nz[1]+1):
lek_z_gh = []
for j in range(nz[2]):
for i in range(nz[3]):
color = ['#1b8680','#48a8a2','#95d5d1','#d7f2f0'][h]
lek_z_ghji = mnm.Tex('%d'%zh_ruam[g,h,j,i],font_size=54,color=color)
tamnaeng = [i+4.5+g*0.5,-j+(g-1)*1.9,0]
lek_z_ghji.move_to(tamnaeng)
lek_z_gh.append(lek_z_ghji)
if(h==0):
tamnaeng[0] += 3.5
krop_z_ghji = mnm.Square(1,fill_opacity=1,fill_color='#9a2c36',color='#541117')
krop_z_ghji.move_to(tamnaeng)
krop_z_g.append(krop_z_ghji)
lek_z_g.append(mnm.VGroup(*lek_z_gh))
lek_z.append(mnm.VGroup(*lek_z_g))
krop_z.append(mnm.VGroup(*krop_z_g))
lek_b_g = mnm.Tex('%+d'%b[g],font_size=42,color='#d2feb1')
tamnaeng = [4.5+g,3+1./3,0]
lek_b_g.move_to(tamnaeng)
lek_b.append(lek_b_g)
krop_b_g = mnm.Square(1,fill_opacity=1,fill_color='#aa81c9',color='#50296f')
krop_b_g.move_to(tamnaeng)
krop_b.append(krop_b_g)
lek_z = mnm.VGroup(*lek_z)
krop_z = mnm.VGroup(*krop_z)
lek_b = mnm.VGroup(*lek_b)
krop_b = mnm.VGroup(*krop_b)
for h in [2,1,0]:
self.add(krop_x[h],lek_x[h])
for g in [2,1,0]:
self.add(krop_k[g][h],lek_k[g][h])
for g in [2,1,0]:
self.add(krop_z[g],krop_b[g],lek_b[g])
for g in range(3):
lis_play1 = [
krop_z[g].animate.shift([-3.5,0,0]),
mnm.ReplacementTransform(lek_b[g].copy(),lek_z[g][0]),
]
for h in [2,1,0]:
lis_play1.extend([
krop_k[g][h].animate.shift([4.5-g*0.5/3,-g*0.5+1,0]),
lek_k[g][h].animate.shift([4.5-g*0.5/3,-g*0.5+1,0]),
])
self.play(*lis_play1,run_time=0.5)
lis_play2 = [
krop_b[g].animate.shift([0,2./3,0]),
lek_b[g].animate.shift([0,2./3,0]),
]
for h in [2,1,0]:
lis_play2.append(krop_k[g][h].animate.set_fill(opacity=0.2))
self.play(*lis_play2,run_time=0.1)
for h in range(3):
for j in range(nz[2]):
for i in range(nz[3]):
ji = j*nz[3]+i
self.play(
mnm.FadeIn(krop_zk[g][h][ji]),
mnm.ReplacementTransform(lek_k[g][h].copy(),lek_zk[g][h][ji]),
run_time=0.5
)
self.play(
mnm.ReplacementTransform(mnm.VGroup(lek_zk[g][h][ji],lek_z[g][h][ji]),lek_z[g][h+1][ji]),
mnm.FadeOut(krop_zk[g][h][ji]),
run_time=0.5
)
if(i!=nz[3]-1):
luean = [1,0,0]
else:
luean = [1-nz[3],-1,0]
if(ji!=nz[2]*nz[3]-1):
self.play(
lek_k[g][h].animate.shift(luean),
krop_k[g][h].animate.shift(luean),
run_time=0.25
)
if(h!=2):
self.play(
krop_x[h].animate.shift([0,-3,0]),
lek_x[h].animate.shift([0,-3,0]),
run_time=0.25
)
else:
self.play(
krop_x[1].animate.shift([0,3,0]),
lek_x[1].animate.shift([0,3,0]),
krop_x[0].animate.shift([0,3,0]),
lek_x[0].animate.shift([0,3,0]),
run_time=0.25
)
self.play(
krop_k[g][h].animate.set_fill(opacity=1),
run_time=0.05)
self.play(
krop_k[g][h].animate.shift([-8+g*0.5/3,g*0.5,0]),
lek_k[g][h].animate.shift([-8+g*0.5/3,g*0.5,0]),
run_time=0.2
)
lis_play3 = [
krop_b.animate.shift([0,-2./3,0]),
lek_b.animate.shift([0,-2./3,0])
]
for h in [2,1,0]:
for g in [2,1,0]:
lis_play3.extend([
krop_k[g][h].animate.shift([2.5,0,0]),
lek_k[g][h].animate.shift([2.5,0,0]),
])
self.play(*lis_play3,run_time=0.2)
self.wait(0.1)