หน้านี้เขียนขึ้นเพื่อเป็นเนื้อหาเสริมให้กับบทความเรื่องการวิเคราะห์การถดถอยซอฟต์แม็กซ์
https://phyblas.hinaboshi.com/20161205 ฟังก์ชันซอฟต์แม็กซ์เขียนดังนี้
ซึ่งสามารถเขียนฟังก์ชันในไพธอนได้ง่ายๆดังนี้
def softmax(x):
exp_x = np.exp(x)
return (exp_x/exp_x.sum())
โดยที่ x ในที่นี้เป็นอาเรย์หนึ่งมิติซึ่งมีสมาชิกหลายตัว พอป้อนอาเรย์ x ลงไปก็จะได้ผลลัพธ์ออกมาเป็นค่าที่แปลงเป็นความน่าจะเป็น
x = np.array([1,2,5,6])
print(softmax(x))
ได้
[ 0.00483724 0.01314897 0.26410418 0.71790961]
อย่างไรก็ตามมีข้อควรระวังอยู่ นั่นคือเวลาที่ใส่ค่าสูงมากๆลงไป เช่น
x = np.array([1000,1001,1002,1003])
print(softmax(x))
จะได้
[ nan nan nan nan]
ที่เป็นแบบนี้เพราะว่า np.exp(1000) นั้นมีค่ามากเกินไปทำให้ถือว่าเป็นอนันต์
และเมื่อนำค่าอนันต์มาหารกันผลที่ได้ก็จะเป็น nan ซึ่งเป็นความผิดพลาดเนื่องจากข้อจำกัดของคอมพิวเตอร์
ลองใส่ตัวเลขคำนวณดูจริงๆก็จะเห็นปัญหา
print(np.exp(1000)) # ได้ inf
print(np.exp(1000)/np.exp(1000)) # ได้ nan
วิธีการรับมือกับปัญหานี้ก็คือตอนที่คำนวณให้นำค่าตัวที่สูงที่สุดในนั้นมาเป็นฐาน ลบออกจากค่าทุกตัวก่อนที่จะนำมาเข้า exp เท่านี้ค่าก็จะไม่มีวันสูงเกินไปแล้ว
ลองแก้เป็นแบบนี้
def softmax(x):
exp_x = np.exp(x-x.max())
return (exp_x/exp_x.sum())
x = np.array([1000,1001,1002,1003])
print(softmax(x))
ได้
[ 0.0320586 0.08714432 0.23688282 0.64391426]
พอเขียนแบบนี้ก็จะไม่มีปัญหาแล้ว
อีกเรื่องหนึ่งที่ต้องจัดการแก้ก็คือ โดยทั่วไปแล้วข้อมูลที่เรานำมาคำนวณซอฟต์แม็กซ์นั้นมักไม่ได้มีแค่มิติเดียวแต่เป็นสองมิติ
เพราะปกติเวลาที่คำนวณมักจะป้อนข้อมูลหลายแถวในเวลาเดียวกัน และข้อมูลแต่ละแถวก็แบ่งเป็นหลายหลัก โดยทั่วไปการคำนวณจะนำแต่ละหลักในแถวหนึ่งๆมาคำนวณซอฟต์แม็กซ์โดยแต่ละแถวแยกกันโดยไม่เกี่ยวกัน
ตัวอย่าง
x = np.array([[1,3,7,11],[9,1,7,2],[1,2,4,4]])
ถ้านำอาเรย์ x มาใช้กับฟังก์ชันซอฟต์แม็กซ์ที่เพิ่งสร้างขึ้นมาตอนนี้จะเกิดข้อผิดพลาด เนื่องจากถูกทำไว้ให้ใช้กับอาเรย์มิติเดียวเท่านั้น
การเขียนซอฟต์แม็กซ์สำหรับสองมิติจะซับซ้อนขึ้นมาหน่อย โดยเขียนแบบนี้
def softmax(x):
exp_x = np.exp(x.T-x.max(1))
return (exp_x/exp_x.sum(0)).T
เท่านี้ก็ใช้กับสองมิติได้แล้ว ลองนำมาใช้ดู
print(softmax(x))
ได้
[[ 4.45666886e-05 3.29305762e-04 1.79794854e-02 9.81646642e-01]
[ 8.79830446e-01 2.95150233e-04 1.19072103e-01 8.02301516e-04]
[ 2.27845678e-02 6.19348766e-02 4.57640278e-01 4.57640278e-01]]
จะเห็นว่าผลที่ได้นั้นจะแยกคิดเป็นแถวๆ ดังนั้นหากรวมค่าทุกหลักในแต่ละแถวก็จะได้ 1
print(softmax(x).sum(1))
ได้
[ 1. 1. 1.]
เท่านี้ก็ได้ฟังก์ชันซอฟต์แม็กซ์สำหรับนำมาใช้เป็นส่วนประกอบในการเขียนโปรแกรมสำหรับการเรียนรู้ของเครื่องแล้ว
วิธีการทั้งหมดดัดแปลงมาจากเนื้อหาในหนังสือเล่มนี้