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



[python] การเก็บแบบจำลองที่เรียนรู้เสร็จแล้วไว้ใช้งานทีหลัง
เขียนเมื่อ 2017/10/12 08:30
แก้ไขล่าสุด 2021/09/28 16:42
ในบทความที่ผ่านมาได้เขียนถึงการเรียนรู้ของเครื่องไปเยอะพอสมควรแล้ว จะเห็นว่าขั้นตอนการทำงานของโปรแกรมก็คือ
1. สร้างแบบจำลองการเรียนรู้ของเครื่อง (การถดถอยโลจิสติก, โครงข่ายประสาทเทียม, SVM, ฯลฯ)
2. ป้อนข้อมูลให้แบบจำลองไปเรียนรู้
3. นำแบบจำลองที่เรียนรู้เสร็จแล้วมาใช้ทำนายผล

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

แล้วสุดท้ายตอนที่นำผลที่ได้ไปใช้ทำนายนั้น ก็แค่เป็นการใช้ค่าพารามิเตอร์ที่ได้เป็นผลออกมานั้นเพื่อเป็นฟังก์ชันในการคำนวณเพื่อให้ได้ผล จึงใช้เวลาแป๊บเดียว

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

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

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

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

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

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

วิธีการแบ่งคร่าวๆได้เป็น ๒ แบบ คือ
- บันทึกเก็บตัวออบเจ็กต์แบบจำลองเอาไว้
- บันทึกเก็บแค่พารามิเตอร์ที่เรียนรู้ได้

แต่ละวิธีมีข้อดีต่างกันออกไป



เริ่มจากวิธีแรก การบันทึกเก็บตัวแบบจำลอง อาจทำได้ง่ายโดยใช้มอดูลที่ชื่อ pickle ซึ่งเป็นมอดูลมาตรฐานของไพธอนที่ใช้ในการเก็บบันทึกออบเจ็กต์ไว้เพื่อใช้งานข้ามโปรแกรม

ขอยกตัวอย่างโดยใช้ข้อมูล MNIST (รายละเอียดเรื่อง MNIST อ่านได้ใน https://phyblas.hinaboshi.com/20170922)

โดยจะเขียนโปรแกรมการถดถอยโลจิสติกโดยใช้คลาส ThotthoiLogistic ที่ได้เขียนไว้ใน https://phyblas.hinaboshi.com/20171006

ตัวไฟล์ที่เก็บคลาสโหลดจากในนี้ได้ https://gist.github.com/phyblas/e65d8f2dc813b0d9289431a1061428fb

ตัวอย่างในบทความนี้ใช้แบบจำลองการถดถอยโลจิสติกเป็นตัวอย่าง แต่ก็สามารถใช้กับแบบจำลองการเรียนรู้ของเครื่องชนิดอื่นๆ (เช่น โครงข่ายประสาทเทียม, SVM, ฯลฯ) ได้เช่นกัน

ตัวโปรแกรมสำหรับการเรียนรู้เขียนได้ดังนี้
from thotthoilogistic import ThotthoiLogistic # ใช้คลาสที่เขียนขึ้นมาเอง
from sklearn import datasets
from sklearn.model_selection import train_test_split
import pickle

mnist = datasets.fetch_mldata('MNIST original') # โหลดข้อมูล MNIST
X,z = mnist.data/255.,mnist.target
X_fuek,X_thotsop,z_fuek,z_thotsop = train_test_split(X,z,test_size=0.2) # แบ่งข้อมูลฝึกกับทดสอบ
tl = ThotthoiLogistic() # สร้างออบเจ็กต์แบบจำลองจากคลาส
tl.rianru(X_fuek,z_fuek,X_thotsop,z_thotsop,ro=5) # ทำการเรียนรู้

# บันทึกออบเจ็กต์ใส่ลงไฟล์
with open('mnistxz.pickle','wb') as f:
    pickle.dump(tl,f)

ในที่นี้ไฟล์ mnistxz.pickle เป็นไฟล์ไบนารีซึ่งจะถูกสร้างขึ้นมาเพื่อเก็บข้อมูลต่างๆของออบเจ็กต์นี้ ชื่อสกุลจะเป็นอะไรก็ไม่สำคัญ แต่ที่นิยมใช้คือ .pickle หรือ .pkl

จากนั้นในโปรแกรมที่ต้องการนำผลมาใช้ก็เขียนดังนี้
from sklearn import datasets
import pickl

# อ่านข้อมูลออบเจ็กต์ที่เรียนรู้ไว้แล้ว
with open('mnistxz.pickle','rb') as f:
    tl2 = pickle.load(f)

mnist = datasets.fetch_mldata('MNIST original')
X,z = mnist.data/255.,mnist.target

print((tl2.thamnai(X)==z).mean()) # นำมาใช้ทำนายผลและคำนวณความแม่น
# ได้ 0.927857142857

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

และสังเกตได้ว่าไม่จำเป็นต้องทำการ import เรียกใช้คลาส ThotthoiLogistic ไว้ก่อนด้วย เพราะข้อมูลของคลาสก็ถูกเก็บไว้ในไฟล์หมดอยู่แล้ว

จากนั้นก็เรียกใช้เมธอด .thamnai เพื่อทำนายข้อมูลจากตัวแปรต้น X แล้วเทียบเคียงกับผล z เพื่อแปลงเป็นค่าความแม่นยำในการทำนายได้ทันที

พอทำแบบนี้แล้วไฟล์ mnistxz.pickle ก็อาจถือได้ว่าเท่ากับเป็นตัวเก็บฟังก์ชันสำหรับใช้คำนวณทำนายผล คือโหลดมาแล้วใช้คำนวณได้เลย ไม่ต้องสนว่ามันมาจากไหน แบบนี้บางทีเราอาจส่งไฟล์นี้ให้คนอื่นไปใช้งานก็ยังได้

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

นอกจากนี้ยังมีอีกวิธีคืออาจใช้คำสั่ง joblib ในมอดูลย่อย externals ของ sklearn ก็ได้

หากใช้แล้วการเขียนจะดูสั้นลง นั่นคือเวลาจะบันทึกก็แค่
from sklearn.externals import joblib
joblib.dump(tl,'mnistxz.pickle')

ส่วนเวลาจะโหลดก็
from sklearn.externals import joblib
tl2 = joblib.load('mnistxz.pickle')

ผลที่ได้จะไม่ต่างจากใช้ pickle คู่กับ open แค่เขียนสั้นลง



ต่อมาดูอีกวิธีก็คือการเก็บแค่พารามิเตอร์ไว้เพื่อนำมาใช้

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

ยกตัวอย่างเช่นกรณีของการถดถอยโลจิสติก เราอาจต้องการนำผลที่ได้จากตัวอย่างก่อนหน้านี้ไปใช้กับคลาส LogisticRegression ของ sklearn (เกี่ยวกับคลาสนี้อ่านได้ที่ https://phyblas.hinaboshi.com/20171010)

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

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

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

วิธีการเขียนอาเรย์ลงไฟล์อ่านได้ใน https://phyblas.hinaboshi.com/numa39

ในการบันทึกค่าน้ำหนักนั้นอาจใช้คำสั่ง np.savetxt หรือ np.save ก็ได้

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

หากใช้วิธีนี้แล้วจะทั้งโหลดเร็วและใช้พื้นที่น้อย ไฟล์จะเล็กกว่าการเก็บทั้งออบเจ็กต์ด้วย เพราะว่าเก็บแค่ข้อมูลพารามิเตอร์ ไม่ต้องเก็บข้อมูลอื่นๆของคลาสหรือตัวออบเจ็กต์

ดังนั้นในที่นี้จะใช้ np.save เพื่อบันทึกเก็บค่าพารามิเตอร์

โค้ดในส่วนของการเรียนรู้
from thotthoilogistic import ThotthoiLogistic # ใช้คลาสที่เขียนขึ้นมาเอง
from sklearn import datasets
from sklearn.model_selection import train_test_split
import numpy as np

# ตรงส่วนนี้ซ้ำกับด้านบน
mnist = datasets.fetch_mldata('MNIST original')
X,z = mnist.data/255.,mnist.target
X_fuek,X_thotsop,z_fuek,z_thotsop = train_test_split(X,z,test_size=0.2)
tl = ThotthoiLogistic()
tl.rianru(X_fuek,z_fuek,X_thotsop,z_thotsop,ro=5)

np.save('mnistxz.npy',tl.w) # บันทึกค่าพารามิเตอร์

ตัวค่าพารามิเตอร์นี้ก็ถูกเก็บอยู่ในแอตทริบิวต์ .w นั่นเอง หากเราลองมาดู
print(tl.w.shape) # ได้ (785, 10)

พารามิเตอร์นี้เป็นอาเรย์สองมิติที่มีขนาดเป็น (จำนวนตัวแปรต้น+1, จำนวนกลุ่มที่แบ่ง)

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

หากจะเอาแค่น้ำหนักจะเป็น tl.w[1:] ส่วนไบแอสคือ tl.w[0]

ไบแอสจะเป็นอาเรย์มิติเดียว มีขนาดเท่ากับจำนวนกลุ่มที่แบ่ง

ค่าตัวเลขทั้งหมด (784+1)×10 = 7850 ตัวนี้ล่ะค่าที่สำคัญที่เราต้องการจริงๆ เพราะต้องเอามาใช้ในการคำนวณเพื่อทำนาย

แต่ว่า sklearn มีการแยกค่าน้ำหนักและไบแอสเป็นคนละตัวแปรกัน ดังนั้นจึงต้องแยกใส่

การนำพารามิเตอร์มาใส่ให้กับ sklearn เพื่อใช้งานทำได้ดังนี้
from sklearn import datasets
from sklearn.linear_model import LogisticRegression as Lori
import numpy as np

mnist = datasets.fetch_mldata('MNIST original')
X,z = mnist.data/255.,mnist.target

w = np.load('mnistxz.npy') # โหลดข้อมูลค่าน้ำหนักและไบแอส
lori = Lori()
lori.coef_ = w[1:].T # ป้อนค่าน้ำหนัก
lori.intercept_ = w[0] # ป้อนค่าไบแอส
lori.classes_ = np.arange(w.shape[1]) # ป้อนค่าจำนวนกลุ่มที่แบ่ง
print((lori.predict(X)==z).mean()) # ใช้ทำนายผล หาความแม่น
#print(lori.score(X,z)) # จะใช้แบบนี้ก็ได้ ค่าเท่ากัน
# ได้ 0.927857142857

ในที่นี้ .coef_ คือค่าน้ำหนัก ส่วน .intercept_ คือไบแอส นอกจากนี้จำนวนกลุ่มที่แบ่ง .classes_ ก็เป็นสิ่งที่ต้องระบุเพิ่มเข้าไปด้วย

เมื่อป้อนค่าทั้ง ๓ นี้เข้าไปแล้วก็จะสามารถใช้เมธอด predict เพื่อทำการทำนายค่าได้ทันทีโดยที่ไม่ต้องมีการใช้เมธอด fit เพื่อทำการเรียนรู้ก่อนเลย ผลการทำนายที่ได้ก็เหมือนกับที่ใช้คลาสที่สร้างขึ้นเองทำนาย

แน่นอนว่าสามารถทำกลับกัน คือเรียนรู้ด้วย sklearn แล้วก็เอาผลไปใช้ในคลาสของเราเองก็ได้

สรุปง่ายๆก็คือ สิ่งที่สำคัญที่สุดก็คือค่าพารามิเตอร์ที่เป็นผลสำเร็จจากการเรียนรู้

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

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

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

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

เช่น ในการเรียนรู้ข้อมูล MNIST ข้อมูลที่ป้อนเข้าไปเพื่อเรียนรู้มีจำนวน 70,000×784 = 54,880,000 ตัว แต่ผลสำเร็จสุดท้ายที่โปรแกรมทำการบันทึกไว้จริงๆคือพารามิเตอร์เพียง 7850 ตัวที่เรียนรู้ได้มาเท่านั้น

ตรงนี้ก็แสดงให้เห็นถึงความที่เรียบง่ายแต่แฝงไปด้วยความสามารถของเทคนิคการเรียนรู้ของเครื่อง



อ้างอิง
https://morvanzhou.github.io/tutorials/machine-learning/sklearn/3-5-save


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

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

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

หมวดหมู่

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

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

สารบัญ

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

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

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



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

  ค้นหาบทความ

  บทความแนะนำ

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

ไทย

日本語

中文