φυβλαςのβλογ
บล็อกของ 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)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

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

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月

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

ไทย

日本語

中文