φυβλαςのβλογ
บล็อกของ phyblas



[python] การเรกูลาไรซ์เพื่อป้องกันการเรียนรู้เกิน
เขียนเมื่อ 2017/09/28 07:59
แก้ไขล่าสุด 2021/09/28 16:42
ก่อนหน้านี้ได้พูดถึงการป้องกันการเรียนรู้เกินด้วยการแบ่งข้อมูลส่วนหนึ่งมาใช้ตรวจสอบ https://phyblas.hinaboshi.com/20170924

คราวนี้จะพูดถึงอีกวิธีหนึ่งในการแก้ปัญหาการเรียนรู้เกิน ก็คือการเรกูลาไรซ์ (正规化, regularize)

คำนี้หากแปลเป็นไทยตรงๆก็คือการทำให้เป็นแบบแผนหรือเป็นปกติทั่วไป แต่ความหมายในที่นี้ก็คือ การทำให้ค่าพารามิเตอร์น้ำหนักบางตัวภายในโมเดลไม่สูงมากจนเกินไป

ที่ค่าน้ำหนักบางตัวมากเกินแล้วจะไม่ดี นั่นเพราะจะทำให้ผลลัพธ์ขึ้นกับตัวแปรที่มีน้ำหนักเยอะนั่นมากเกินไป ดังนั้นถ้าหากค่าตัวนั้นอยู่ดีๆเปลี่ยนไปมาก ผลลัพธ์การทายก็อาจจะพลิกกลับทันทีเลย

ยกตัวอย่างเช่นว่า ในสังคมกลุ่มที่เราอยู่นั้น พบว่าคนที่เขียนไพธอนส่วนใหญ่นิสัยดี ดังนั้นเลยทำให้เกิดอคติขึ้นมาว่า ถ้าเจอคนเขียนไพธอนแสดงว่าคนนั้นน่าจะนิสัยดี

แต่ว่านั่นอาจจะเป็นแค่เพราะว่าในสังคมที่เราอยู่บังเอิญเจอคนเขียนไพธอนที่นิสัยดีเยอะเท่านั้นเอง ถ้าออกมาสู่สังคมอื่นอาจจะเจอคนเขียนไพธอนที่นิสัยไม่ดีเยอะก็เป็นได้

นี่เป็นลักษณะของความโน้มเอียงที่เกิดจากข้อมูลกลุ่มใดกลุ่มหนึ่งที่มีอยู่จำกัด ถ้าหลงไปตัดสินปรับเปลี่ยนระบบความคิดการติดสินของตัวเองจากข้อมูลที่มีอยู่แค่นั้นก็อาจจะทำให้เราประเมินข้อมูลกลุ่มใหม่ได้ผิด

ดังนั้นการเรกูลาไรซ์ในที่นี้จึงเปรียบเหมือนเป็นการลดการสร้างอคติ ให้เราไม่รีบด่วนตัดสินอะไรอย่างสุดขั้วจากแค่ข้อมูลที่มีอยู่

คนเรามักจำเป็นต้องตัดสินอะไรจากประสบการณ์ตรงที่ตัวเองเคยเจอและข้อมูลที่มีในมือ นั่นไม่ใช่เรื่องผิดอะไร เพราะข้อมูลที่มีมักถูกจำกัดด้วยฐานะและเวลา

แต่ควรเผื่อใจไว้ว่าบางทีอาจเจอสิ่งใหม่ที่ไม่เป็นไปตามนั้นก็เป็นได้ ยิ่งถ้าเจอสังคมใหม่ที่แปลกไปจากเดิมมากๆ

ยังไงการตัดสินอะไรอย่างสุดขั้วจากข้อมูลที่มีอยู่แค่เพียงจำกัดไม่น่าจะใช่เรื่องดี



ที่จริงจะไม่ใช่ปัญหาอะไรมากถ้าหากตัวอย่างที่เราให้โปรแกรมใช้ในการเรียนรู้นั้นมีความครอบคลุมแทบทุกรูปแบบที่เป็นไปได้

แต่ในสถานการณ์ทั่วไปเป็นไปได้ยากที่จะเตรียมตัวอย่างให้ครอบคลุม ดังนั้นจึงต้องมีการป้องกันการเรียนรู้เกิน

ยิ่งข้อมูลที่นำมาใช้ฝึกฝนนั้นมีความหลากหลายไม่พอ ไม่ครอบคลุม การเรียนรู้เกินจะยิ่งเกิดขึ้นได้ง่าย

ขอยกตัวอย่างที่เราสามารถลองเขียนโปรแกรมดูผลได้เห็นชัดเจน ก็คือการแยกแยะตัวเลขจากข้อมูล MNIST ซึ่งได้พูดถึงไปแล้วตั้งแต่หน้า https://phyblas.hinaboshi.com/20170922

ต่อไปจะลองเขียนโค้ดแสดงผลการเรียนรู้ข้อมูล MNIST โดยใช้คลาส ThotthoiLogistic ซึ่งเขียนขึ้นและใช้ไปใน https://phyblas.hinaboshi.com/20170924

ส่วนของโค้ดที่นิยามตัวคลาสสามารถคัดลอกมาจากหน้านี้ได้ https://github.com/phyblas/rianrupython/blob/master/kanrianrukhongkhrueang/thotthoi_mnist_2.py

ลองดึงข้อมูล MNIST ทั้งหมดซึ่งมีถึง 70000 ภาพมาแล้วให้โมเดลของเราอันนี้ทำการเรียนรู้ จากนั้นลองวาดภาพที่แสดงถึงน้ำหนัก
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split

np.random.seed(0)
mnist = datasets.fetch_mldata('MNIST original')
X,z = mnist.data,mnist.target
X = X/255.
X_fuek,X_truat,z_fuek,z_truat = train_test_split(X,z,test_size=0.2)

eta = 0.24
n_thamsam = 50
n_batch = 100
tl = ThotthoiLogistic(eta)
tl.rianru(X_fuek,z_fuek,n_thamsam,n_batch,X_truat,z_truat)

plt.plot(tl.maen_fuek,'#dd0000')
plt.plot(tl.maen_truat,'#00aa33')
plt.legend([u'ฝึกฝน',u'ตรวจสอบ'],prop={'family':'Tahoma'})

plt.figure()
w = tl.w[1:]
si = plt.get_cmap('seismic')(w/np.abs(w).max()/2+0.5)
for i in range(1,10):
    plt.subplot(330+i)
    plt.imshow(si[:,i].reshape(28,28,4))
plt.show()
print('%.3f%% / %.3f%%'%(max(tl.maen_fuek),max(tl.maen_truat)))


ผลที่ได้จะเห็นแบบนี้ ความแม่นยำในการทายข้อมูลฝึกกับข้อมูลตรวจสอบนั้นต่างกันนิดเดียว แสดงว่าการเรียนรู้เกินเกิดขึ้นน้อย นั่นเพราะจำนวนข้อมูลรวมมีถึง 70000 (ใช้ฝึกฝนจริง 80% = 56000) นั้นเป็นจำนวนที่มากพอสมควร



ส่วนภาพแสดงการกระจายของน้ำหนักนั้น ในที่นี้สีแดงคือบริเวณที่น้ำหนักเป็นบวกมาก คือถ้าหากโดนขีดแล้วจะมีโอกาสเป็นเลขนั้นมาก



ส่วนสีน้ำเงินคือบริเวณที่น้ำหนักติดลบ ถ้าโดนขีดจะไม่น่าเป็น

และสีขาวคือบริเวณที่น้ำหนักเป็น 0 คือแทบไม่สำคัญ แทบไม่ถูกนำมาพิจารณา

จากนั้นลองวาดใหม่โดยคราวนี้สุ่มดึงข้อมูลมาใช้แค่ 1% ก็คือ 700 ภาพ
sumriang = np.random.permutation(len(mnist.target))
X = mnist.data[sumriang[:700]]
z = mnist.target[sumriang[:700]]

นำมาแยกเป็นข้อมูลฝึกฝนและทดสอบ แล้วให้เรียนรู้แบบเดิม แล้ววาดภาพแบบเดิมใหม่ ผลที่ได้ก็คือความแม่นยำในการทายข้อมูลพุ่งขึ้นสูงเกือบ 100% แต่พอทำนายชุดข้อมูลตรวจสอบกลับได้ถูก 83% เท่านั้น



ส่วนภาพแสดงน้ำหนักก็เห็นเป็นเค้าโครงตัวเลขชัดเจนขึ้นมาก



สาเหตุที่เป็นแบบนี้เพราะว่าพอตัวอย่างมีน้อยลง ผลที่ได้ก็เกิดจากการสรุปจากในกลุ่มข้อมูลที่น้อยลง จึงมีความเป็นทั่วไปลดลง ถ้าบังเอิญตัวอย่างที่ยกมานั้นมีการขีดบริเวณนั้นเยอะ ตรงนั้นก็จะเป็นสีแดงได้ง่าย

ซึ่งแบบนี้จะทายผิดได้ง่าย เมื่อเจอตัวอย่างใหม่ที่เส้นไม่ได้ลากไปโดนตรงนั้น

นี่จึงเป็นสิ่งที่แสดงถึงการเรียนรู้เกินอย่างเห็นได้ชัด

ดังนั้นคราวนี้จะมาเขียนคลาสขึ้นใหม่ โดยเพิ่มการเรกูลาไรซ์เข้าไป



วิธีการเรกูลาไรซ์เพื่อไม่ให้ค่าน้ำหนักมากเกินไปก็คือการเพิ่มค่าเสียหายขึ้นตามค่าน้ำหนักขณะนั้น

ฟังก์ชันที่นิยมใช้บวกเพิ่มเข้าไปนั้นมี ๒ แบบคือ l1 กับ l2

l2 คือการคิดค่าเสียหายเพิ่มตามค่าน้ำหนักยกกำลังสอง กล่าวคือ




ในที่นี้ λ คือขนาดของการเรกูลาไรซ์ ยิ่งมากก็จะยิ่งถ่วงให้ค่ำน้ำหนักเพิ่มสูงได้ยาก เป็นพารามิเตอร์หนึ่งที่สำคัญ ต้องพิจารณาเลือกค่าให้เหมาะสม

ส่วน l1 จะคิดค่าเสียหายเพิ่มตามค่าสัมบูรณ์ของน้ำหนัก



ค่าเสียหายส่วนนี้จะไปบวกเพิ่มจากค่าเสียหายเดิมซึ่งมาจากเอนโทรปีไขว้ หรือค่าเฉลี่ยผลรวมความคลาดเคลื่อนกำลังสอง

ส่วนค่าน้ำหนักที่ต้องปรับแก้ในแต่ละรอบนั้นก็ขึ้นกับอนุพันธ์ของค่านี้ กรณี l2 คำนวณค่าอนุพันธ์ได้เป็น



ส่วน l1 นั้นเป็นฟังก์ชันไม่ต่อเนื่อง ต้องแบ่งเป็น ๓ ส่วนคือถ้ามากกว่า 0 จะเป็น λ/n ถ้าน้อยกว่า 0 จะเป็น -λ/n ถ้าเป็น 0 อยู่แล้วก็เป็น 0

หากเขียนเป็นโค้ดก็จะได้แบบนี้
J += 2*w*l/n #l2
J += (w!=0)*np.where(w>0,1,-1)*l/n #l1

ในที่นี้ l แทน λ ส่วน n คือจำนวนข้อมูลฝึก ส่วน J และ w เป็นอาเรย์

J คือค่าเสียหายที่มาจากเอนโทรปีหรือค่าเฉลี่ยผลรวมความคลาดเคลื่อนกำลังสองอยู่ก่อนแล่้ว นำมาบวกเพิ่มค่าเสียหายจาก

ของ l1 นั้น np.where(self.w>0,1,-1) หมายความว่าถ้า w มากกว่า 0 จะเป็น 1 แต่ถ้า w น้อยกว่า 0 จะเป็น -1

ส่วน (self.w!=0) นั้นจะทำให้เป็น 0 เมื่อ w เป็น 0 นอกนั้นจะเป็น 1



เพื่อให้เข้าใจง่ายมาวาดภาพแสดงค่าเสียหายกันให้เห็นภาพชัดว่าพอเพิ่มเรกูลาไรซ์ลงไปในค่าเสียหายแล้วจะเกิดอะไรขึ้น

สมมุติว่าฟังก์ชันค่าเสียหายเดิมของเราเป็นดังระนาบสีน้ำเงินแบบนี้ เป็นแอ่งที่มีจุดต่ำสุดอยู่ที่จุดสีน้ำเงิน คือจุด (1,2)



จากนั้นเพิ่มเรกูลาไรซ์แบบ l2 เข้ามาก็เหมือนเป็นการบวกเพิ่มฟังก์ชันที่มีการบุ๋มตรงจุด (0,0) ดังรูปนี้



พอนำค่าทั้ง ๒ มาบวกกันก็จะได้กลายเป็นระนาบเขียว ซึ่งมีจุดต่ำสุดใหม่ที่ใกล้ใจกลางมากขึ้น คืออยู่ที่ (0.6,1.43)



ทีนี้ถ้าหากเปลี่ยนใหม่มาเป็น l1 จะเป็นยังไง? l1 นั้นคล้ายๆกับ l2 คือทำให้เกิดการยุบตรงกลาง แต่จะเห็นว่าร่องเป็นเหลี่ยม เป็นหุบเขาแหลมชัน



ตรงนี้ที่จุดที่ w1 หรือ w2 เป็น 0 เป็นจุดหักมุมอย่างชัดเจน ดังนั้นพอนำมาบวกกันจึงทำให้จุดต่ำสุดไปเกิดที่ 0 ได้อย่างง่ายได้

ลองรวมกันดู ผลก็คือจุดต่ำสุดไปอยู่ที่ (0.0,1.4) ดูแล้วก็เหมือนกับว่าเป็นการตกร่อง



ดังนั้นลักษณะเด่นของ l1 ก็คือ ทำให้ค่าน้ำหนักบางตัวที่น้อยอยู่แล้วกลายเป็น 0 ได้อย่างง่ายดาย

นี่เป็นตัวอย่างของระบบที่มีค่าน้ำหนักแค่ 2 ตัว ส่วนข้อมูล MNIST มี 784 ตัวอาจมองภาพได้ยากสักหน่อยแต่ก็หลักการเดียวกัน

ภาพข้างต้นนี้วาดโดยโค้ดตามนี้
from mpl_toolkits.mplot3d import Axes3D

x,y = np.meshgrid(np.linspace(-0.5,3.5,401),np.linspace(-0.5,3.5,401))
z = 20+(x-1)**2*3+(y-2)**2*5
l1 = (np.abs(x)+np.abs(y))*6
l2 = (x**2+y**2)*2
z2 = z+l1
# z2 = z+l2

zargmin = z.argmin()
ct = (zargmin//401,zargmin%401)
z2argmin = z2.argmin()
ct2 = (z2argmin//401,z2argmin%401)

plt.figure(figsize=[8,8])
ax = plt.axes([0,0,1,1],projection='3d',xlabel='$w_1$',ylabel='$w_2$')
ax.set_zlim(0,z.max())
ax.plot_surface(x,y,z,rstride=20,cstride=20,alpha=0.2,color='b',edgecolor='k')
ax.scatter(x[ct],y[ct],z.min(),c='b',sizes=[200])
ax.plot([x[ct]]*2,[y[ct]]*2,[0,z.max()],c='b')
# ax.plot_surface(x,y,l2,rstride=20,cstride=20,alpha=0.2,cmap='plasma',edgecolor='k')
# ax.plot_surface(x,y,l1,rstride=20,cstride=20,alpha=0.2,cmap='plasma',edgecolor='k')
# ax.scatter(0,0,0,c='#770077',sizes=[200])
ax.plot_surface(x,y,z2,rstride=20,cstride=20,alpha=0.2,color='g',edgecolor='k')
ax.scatter(x[ct2],y[ct2],z2.min(),c='g',sizes=[200])
ax.plot([x[ct2]]*2,[y[ct2]]*2,[0,z.max()],c='g')
plt.show()


ต่อมา ลองนิยามคลาสของโมเดลใหม่ที่ได้เพิ่มการเรกูลาไรซ์นี้ขึ้นมา จากนั้นก็นำมาใช้เพื่อเรียนรู้ข้อมูลชุดเดิม ลองให้ λ เป็น 80 แล้วใช้การเรกูลาไรซ์แบบ l2

λ ในที่นี้แทนด้วยตัวแปร l ส่วนรูปแบบของการเรกูลาไรซ์แทนด้วยตัวแปร reg โดยจะมีค่าได้แค่ ๒ แบบคือ l1 และ l2 ทั้งสองอย่างนี้ให้กำหนดตั้งแต่ตอนสร้างคลาสขึ้นเลย แต่ถ้าไม่ใส่ก็จะถือว่าเป็น 0 คือไม่มีการเรกูลาไรซ์
def softmax(x):
    exp_x = np.exp(x.T-x.max(1))
    return (exp_x/exp_x.sum(0)).T

class ThotthoiLogistic:
    def __init__(self,eta,reg='l2',l=0):
        self.eta = eta # อัตราการเรียนรู้
        self.reg = reg # รูปแบบการเรกูลาไรซ์
        self.l = l # ขนาดของเรกูลาไรซ์

    def rianru(self,X,z,n_thamsam,n_batch=0,X_truat=0,z_truat=0,romaiphoem=0):
        n = len(z)
        if(type(X_truat)!=np.ndarray): # ถ้าไม่ได้ป้อนข้อมูลตรวจสอบมาด้วย ก็ให้ใช้ข้อมูลฝึกฝนเป็นข้อมูลตรวจสอบ
            X_truat,z_truat = X,z
        if(n_batch==0 or n<n_batch):
            n_batch = n
        self.kiklum = int(z.max()+1) # จำนวนผลลัพธ์
        z_1h = z[:,None]==range(self.kiklum) # แปลงเป็น one-hot
        self.w = np.zeros([X.shape[1]+1,self.kiklum]) # ค่าน้ำหนักตั้งต้นเป็น 0
        self.dw = self.w.copy() # สร้างอาเรย์สำหรับพักค่าการเปลี่ยนแปลงน้ำหนักด้วย
        
        self.khasiahai = [] # ลิสต์บันทึกค่าเสียหาย (เอนโทรปี+เรกูลาไรซ์)
        self.maen_fuek = [] # ลิสต์บันทึกค่าความแม่นยำในการทำนายข้อมุลฝึก
        self.maen_truat = [] # ลิสต์บันทึกค่าความแม่นยำในการทำนายข้อมุลตรวจสอบ
        disut = 0 # ค่าความแม่นยำดีสุดที่ได้
        maiphoem = 0 # นับว่าความแม่นยำไม่เพิ่มมาแล้วกี่ครั้ง
        for j in range(n_thamsam):
            lueak = np.random.permutation(n)
            for i in range(0,n,n_batch):
                Xn = X[lueak[i:i+n_batch]]
                zn = z_1h[lueak[i:i+n_batch]]
                phi = self.ha_softmax(Xn)
                eee = (zn-phi)/len(zn)
                self.dw[1:] = np.dot(eee.T,Xn).T
                self.dw[0] = eee.sum(0)
                # หาก l ไม่เป็น 0 ให้ปรับค่าน้ำหนักตามผลจากการเรกูลาไรซ์ด้วย
                if(self.l>0):
                    if(self.reg=='l1'):
                        self.dw[1:] -= (self.w[1:]!=0)*np.where(self.w[1:]>0,1,-1)*self.l/n
                    else: # l2
                        self.dw[1:] -= 2*self.w[1:]*self.l/n
                self.w += self.dw*self.eta
            
            thukmai = self.thamnai(X)==z
            maen_fuek = thukmai.mean()*100
            thukmai = self.thamnai(X_truat)==z_truat
            maen_truat = thukmai.mean()*100
            khasiahai = self.ha_entropy(X,z_1h) # ค่าเสียหายจากเอนโทรปี
            # หาก l ไม่เป็น 0 ให้บวกเรกูลาไรซ์เพิ่มเข้าไปในค่าเสียหายด้วย
            if(self.l>0):
                if(reg=='l1'):
                    khasiahai += self.l*np.abs(self.w[1:]).sum()
                else: # l2
                    khasiahai += self.l*((self.w[1:])**2).sum()
            
            if(maen_truat > disut):
                # ถ้าจำนวนที่ถูกมากขึ้นกว่าเดิมก็บันทึกค่าจำนวนนั้น และน้ำหนักในตอนนั้นไว้
                disut = maen_truat
                maiphoem = 0
                w = self.w.copy()
            else:
                maiphoem += 1 # ถ้าไม่ถูกมากขึ้นก็นับไว้ว่าไม่เพิ่มไปอีกครั้งแล้ว
            
            self.maen_fuek.append(maen_fuek)
            self.maen_truat.append(maen_truat)
            self.khasiahai.append(khasiahai)
            print(u'ครั้งที่ %d ถูก %.3f%% สูงสุด %.3f%% ไม่เพิ่มมาแล้ว %d ครั้ง'%(j+1,self.maen_truat[-1],disut,maiphoem))
            
            if(romaiphoem!=0 and maiphoem>=romaiphoem):
                break # ถ้าจำนวนที่ถูกไม่เพิ่มเลย 10 ครั้งก็เลิกทำ
                
        self.w = w # ค่าน้ำหนักที่ได้ในท้ายสุด เอาตามค่าที่ทำให้ทายถูกมากที่สุด

    def thamnai(self,X):
        return (np.dot(X,self.w[1:])+self.w[0]).argmax(1)

    def ha_softmax(self,X):
        return softmax(np.dot(X,self.w[1:])+self.w[0])

    def ha_entropy(self,X,z_1h):
        return -(z_1h*np.log(self.ha_softmax(X)+1e-7)).mean()
   
eta = 0.2
n_thamsam = 100
n_batch = 100
reg = 'l2'
l = 800
tl = ThotthoiLogistic(eta,reg,l)
tl.rianru(X_fuek,z_fuek,n_thamsam,n_batch,X_truat,z_truat)

# โค้ดส่วนวาดกราฟขอละไว้ ไปดึงจากด้านบนมาใช้

ผลที่ได้ลองเอามาเปรียบเทียบกับผลก่อนหน้าซึ่งไม่มีการเรกูลาไรซ์ก็จะพบว่าความแม่นของการทำนายชุดข้อมูลตรวจสอบไม่ได้ต่างจากเดิมมาก แต่ของชุดข้อมูลฝึกฝนลดลงอย่างมาก แสดงว่าการเรียนรู้เกินลดลงไปมาก




เพียงแต่ว่าค่า λ (ในที่นี้คือ l) นั้นจำเป็นต้องปรับให้เหมาะสม หากมากไปก็กลับจะทำให้การเรียนรู้ไม่คืบหน้า เพราะน้ำหนักจะเข้าใกล้ 0 กันมากไป กลับกลายเป็นผลเสีย

เช่นลองให้ l = 800 คราวนี้จะพบว่าผลการทายกลายเป็นไม่แม่นไปเลย





ต่อมาลองดูแบบ l1 บ้าง ลองแก้ reg เป็น l1 แล้วก็วาดภาพแสดงการกระจายของค่าน้ำหนัก
tl = ThotthoiLogistic(eta=0.24,reg='l1',l=5)
tl.rianru(X_fuek,z_fuek,50,100,X_truat,z_truat)

จะได้ภาพแบบนี้ ซึ่งเห็นได้ว่าบริเวณสีขาวมีมากขึ้น คือถูกลดค่ากลายเป็น 0 เห็นได้ชัดเจน



นี่แสดงให้เห็นถึงลักษณะเด่นของ l1 ที่ว่าจะทำให้เกิดค่าน้ำหนักเป็น 0 มาก



อ้างอิง


-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> ปัญญาประดิษฐ์
-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python >> numpy
-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python >> matplotlib

ไม่อนุญาตให้นำเนื้อหาของบทความไปลงที่อื่นโดยไม่ได้ขออนุญาตโดยเด็ดขาด หากต้องการนำบางส่วนไปลงสามารถทำได้โดยต้องไม่ใช่การก๊อปแปะแต่ให้เปลี่ยนคำพูดเป็นของตัวเอง หรือไม่ก็เขียนในลักษณะการยกข้อความอ้างอิง และไม่ว่ากรณีไหนก็ตาม ต้องให้เครดิตพร้อมใส่ลิงก์ของทุกบทความที่มีการใช้เนื้อหาเสมอ

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
ภาษา javascript
ภาษา mongol
ภาษาศาสตร์
maya
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  ค้นหาบทความ

  บทความแนะนำ

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

บทความแต่ละเดือน

2024年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2023年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2022年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2021年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2020年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

ค้นบทความเก่ากว่านั้น

ไทย

日本語

中文