Category Archives: How to

ทดลองแบบแมวๆ ใช้ Magenta บน Docker ช่วยแต่งเพลง

Magenta เป็นโปรแกรมที่ผลิตงานศิลปะ แต่งเพลงได้ด้วย machine มันใช้งาน TensorFlow แต่ผมลงไม่สำเร็จ เลยใช้แบบเร็วๆ แบบแมวๆ ด้วยการใช้ Docker เสียเลย ซึ่งผมลง Docker ไว้แล้ว (ลืมไปแล้วว่าลงด้วยวิธีไหน) ก็สั่ง

docker run -it -p 6006:6006 -v /tmp/magenta:/magenta-data tensorflow/magenta

จะเข้าสู่การใช้งานเชลล์บน Docker ก็จะใช้ Magenta แต่ต้องมีทวงทำนองนำร่องไปก่อน ลองเล่นเอง
C, D, E, C,
D, E, G, E
ก็เพราะดีนะครับ ใน Docker ก็สั่ง

# melody_rnn_generate \
   --config=lookback_rnn \
   --bundle_file=/magenta-models/lookback_rnn.mag \
   --output_dir=/magenta-data/lookback_rnn/generated \
   --num_outputs=10 \
   --num_steps=128 \
   --primer_melody="[60, -2, 62, -2, 64, -2, 60, -2, 62, -2, 64, -2, 67, -2, 64, -2]"

ผมใช้แบบ Melody RNN สร้างเพลงแบบเล่นโน้ตที่ละตัว แบบที่ violin หรือ flute เล่นได้ (นั้นคือเล่นคอร์ดไม่ได้ แต่ Magenta ก็มีให้เลือกหลายแบบนะครับ velocity ก็มี)
โดย –primer_melody ใส่เลขที่เป็นตัวโน้ต ตามรหัสของ MIDI โดย 60 คือ middle C แต่ละโน้ตตามด้วย โน้ตตัวอื่นก็บวกลบหนึ่ง เป็นการเพิ่มหรือลดที่ละ semitone ส่วน -2 คือ no event

ก็จะได้เพลงเป็น MIDI ไฟล์ อยู่ที่เครื่องตัวหลักของเรา (นอก Docker) ที่ path /tmp/magenta/lookback_rnn/generated/

ทางเลือกในการตัดคำภาษาไทย ด้วย Python

ประโยคในภาษาไทยไม่ได้มีการเว้นวรรคระหว่างคำ การจะตัดคำในประโยคจึงต้องใช้ algorithm มาหาว่าตรงไหนเป็นคำ แล้วจึงตัด ก็ไปเจออยากน้อยสองที่ซึ่งใช้ได้ กับภาษา Python คือ PyThaiNLP และ deepcut ท่าทาง machine learning ได้ประโยชน์ในงานนี้ล่ะครับ

PyThaiNLP
https://python3.wannaphong.com/2017/05/pythainlp-%e0%b9%82%e0%b8%a1%e0%b8%94%e0%b8%b9%e0%b8%a5-nlp-%e0%b8%a0%e0%b8%b2%e0%b8%a9%e0%b8%b2%e0%b9%84%e0%b8%97%e0%b8%a2%e0%b9%83%e0%b8%99-python.html
https://github.com/wannaphongcom/pythainlp

ผมใช้กับ Python 3 นะครับ ติดตั้งโดยสั่ง

pip3 install pythainlp

ทดลองสคริป Python (จริงๆ ก็ทำคล้ายๆ ของเจ้าของผลงานครับ)

from pythainlp.segment import segment
s=segment('ฉันรักภาษาไทยเพราะฉันเป็นคนไทย')
print(s)

ได้ออกมาว่า

['ฉัน', 'รัก', 'ภาษา', 'ไทย', 'เพราะ', 'ฉัน', 'เป็น', 'คน', 'ไทย']

ถ้าเป็น Python 2 ก็ใช้ได้เหมือนกันครับ แต่ต้องระบุสตริงเป็น Unicode ด้วย ใส่ u ข้างหน้าสตริง (u’ฉันรักภาษาไทยเพราะฉันเป็นคนไทย’) ส่วนผลลัพธ์เมื่อพิมพ์อาจจะออกมาเป็นโค้ดนะครับ เช่น u’\u0e09\u0e31\u0e19′ คือ ‘ฉัน’

deepcut
https://www.facebook.com/groups/988867541235062/permalink/1258831837571963/
https://github.com/rkcosmos/deepcut

ติดตั้งบน Python 3 บ้าง

pip3 install deepcut

ซึ่งรอนานเอาเรื่องเลยครับ จากนั้นก็ทดลองใช้งาน

from deepcut import tokenize 
tokenize('ฉันรักภาษาไทยเพราะฉันเป็นคนไทย')

ก็ได้ผลลัพธ์เหมือนกัน เลยลองใช้ทั้งสอบแบบในสคริปเดียวกันดู
(พึ่งเห็นว่ายังไม่ได้คอมไพล์ TensorFlow ให้ไปใช้ความสามารถของชุดคำสั่ง SSE 4.1, SSE 4.2 และ AVX อันที่จริง GPU ค่ายเขียวในเครื่องก็มีนะครับ แต่ต้องสมัครลงทะเบียน cuDNN)

แล้วก็ รวมตัวเลือก https://github.com/kobkrit/nlp_thai_resources/blob/master/README.md

หาจำนวน Fibonacci ด้วย Matrix

ตัวเลขฟิโบนักซี ซึ่งเป็นความสัมพันธ์เวียนบังเกิดที่มีสูตร

f(n)=f(n-1)+f(n-2); f(1)=f(2)=1

สมมติอยากรู้ f(3) ถ้าเขียนในรูป matrix (ขอเขียนแบบ Python list นะครับ) จะได้ว่า

[[f(3)]] = [[1, 1]] * [[f(2)], [f(1)]] = [[f(2) + f(1)]]

\left[ \begin{array}{cc} f(3) \end{array} \right] = \left[ \begin{array}{cc} 1 & 1 \end{array} \right] * \left[ \begin{array}{cc} f(2) \\ f(1) \end{array} \right] = \left[ \begin{array}{cc} f(2) + f(1) \end{array} \right]

ซึ่ง matrix ตัวหนึ่ง เก็บค่าตัวคูณ (สัมประสิทธิ์) ซึ่งก็เป็นค่าคงที่ไม่ขึ้นกับค่า n ส่วนอีก matrix เก็บเลข fibonacci ตัวก่อนๆ เพื่อใช้คำนวณตามสูตร

แต่ถ้าต้องการจะรู้ตัวถัดไป คือ f(4) จะมีปัญหาว่า matrix ที่เราได้ มีแค่ f(3) โดยไม่่มี f(2) งานนี้เราจึงต้องคูณ matrix แล้วเก็บค่าเก่าที่จำเป็นในการหาเลข fibonacci ตัวต่อไปด้วย คือเก็บ f(2) วิธีง่ายๆ ก็แค่คูณ 1 เก็บไว้ ที่เหลือ คูณ 0

\left[ \begin{array}{cc} f(3) \\ f(2) \end{array} \right] = \left[ \begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array} \right] * \left[ \begin{array}{cc} f(2) \\ f(1) \end{array} \right] = \left[ \begin{array}{cc} f(2) + f(1) \\ f(2) + 0 \end{array} \right]

\left[ \begin{array}{cc} f(4) \\ f(3) \end{array} \right] = \left[ \begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array} \right] * \left[ \begin{array}{cc} f(3) \\ f(2) \end{array} \right]

ถ้าแทน F(n) = \left[ \begin{array}{cc} f(n) \\ f(n-1) \end{array} \right] และ M = \left[ \begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array} \right] จะได้ว่า

F(3) = M*F(2)
F(4) = M*F(3) = M*M*F(2) = M2F(2)
F(5) = M*F(4) = M*M*M*F(2) = M3F(2)

น้้นคือ

F(n) = M(n-2)F(2) ; F(2) = \left[ \begin{array}{cc} f(2) \\ f(1) \end{array} \right] = \left[ \begin{array}{cc} 1 \\ 1 \end{array} \right]

จะเห็นว่า เราเปลี่ยนรูปความสัมพันธ์จากการบวกไปเรื่อย มาเป็นการยกกำลังหรือการคูณเลข (หรือ matrix) ตัวเดียวกันหลายๆ ครั้ง
ซึ่งเป็นการคูณที่มักจะใช้ขั้นตอนการคำนวณมากกว่าการบวก การเปลี่ยนจากการบวกมาเป็นการยกกำลังนั้นน่าจะคำนวณยุ่งยากกว่า แต่การยกกำลังนั้น เราไม่จำเป็นต้องใช้อัลกอริทึมที่มีขั้นตอนอยู่ใน O(n) แต่ทำแบบ O(log(n)) ได้ โดยอาศัยหลักการที่ว่า Mn = (Mn/2)2 คือถ้าเราจะหา Mn ก็หใ้หา Mn/2 แล้วเอามายกกำลังสอง งานนี้เขียนโปรแกรมแบบ recursive จนกว่าจะถึง M หรือ M2 ก็หยุดเรียกตัวเอง ถ้าเกิด n เป็นเลขคี่ นั้นคือ n-1 เป็นเลขคู่ ก็ใช้สูตร Mn = M*Mn-1 = M*(M(n-1)/2)2

แนวทางจาก http://fusharblog.com/solving-linear-recurrence-for-programming-contest/

วิธีแปลง NFA-Λ ให้ไปเป็น NFA

ต่อจากตอนที่แล้ว ซึ่งถ้าเจอเส้นที่เป็น Λ (บางตำราใช้ตัวอักษร ε แทน) เราต้องหาวิธีแปลงให้ NFA-Λ ให้ไม่มี Λ ไปเป็น NFA ที่ใช้วิเคราะห์ string เดียวกันได้

ขอนิยาม Λ(a) หมายถึง closure ที่ input เป็น set a แบบที่มีสมาชิกเป็น state และ output จาก Λ(a) จะเป็น set ของ state ที่สามารถเดินทางไปถึงได้จาก state ที่อยู่ใน a ด้วยเส้น Λ ทั้งนี้ให้ถือว่า state ใน a สามารถเดินทางไปหา a ได้เสมอ (สมาชิกทุกตัวของ a จะเจอใน Λ(a) เสมอ) และต้องมองข้ามช็อตหลายชิ่ง ถ้า state ปลายทางที่ไปถึงด้วย Λ นั้น มีไปต่อด้วย Λ ไปยัง state อื่นๆ ได้อีก ต้องรวม state นั้นๆ เพิ่มเข้าไปด้วย

การแปลงจาก NFA-Λ ไปเป็น NFA นั้นจำนวน state และชื่อ state จะไม่เปลี่ยน วิธีการหาว่า state หนึ่งๆ ใน NFA-Λ นั้น เมื่อได้รับ input แล้วจะไปยัง state ไหนของ NFA บ้าง ขั้นแรกให้เราหา Λ({q}) ของ state q นั้นๆ จากนั้นให้แยกออกไปตาม input ในแต่ละแบบ (ไม่รวม Λ) ว่า จากทุก state ที่ Λ({q}) ตอบค่ากลับมานั้น ไปยัง state ไหนได้บ้าง สมมติให้เป็น set p จะได้ว่าจาก state q เมื่อใส่ input นั้นๆ จะไป state ที่ตอบกลับมาโดย Λ(p)

สำหรับ state เริ่มต้นนั้นไม่เปลี่ยน ส่วน accept state ของ NFA ที่แปลงได้มาจาก NFA-Λ นั้นเหมือนเดิมแต่จะรวม state เริ่มต้นเข้าไปด้วย ถ้า closure ของ state เริ่มต้นนั้นมี accept state ปะปนอยู่ด้วย ขอยกตัวอย่าง ซึ่งทำเป็นตาราง คงเห็นภาพมากกว่า

(ตัวอย่างรอแป๊ปนะครับ)

อ้างอิง http://www.cs.odu.edu/~toida/nerzic/390teched/regular/fa/nfa-lambda-2-nfa.html

 

วิธีแปลง NFA ให้ไปเป็น FA

วิธีแปลง Non-deterministic Finite Automata (NFA) ให้เป็น Deterministic Finite Automata (DFA) แต่ผมขอเรียกสั้นๆ ว่า FA นะครับ (เพราะมันไม่มี N) ทั้งนี้ ถ้ามีเส้นที่เป็น Λ คือเส้นซึ่งสามารถไปต่อได้โดยไม่ต้องอ่านตัวอักษรเข้ามา (ค่อยไปอ่านที่หลังใน state ถัดๆ ไป) ก็คือ NFA ของเราเป็นแบบ NFA-Λ ก็ต้องเปลี่ยนมันให้เป็น NFA ที่ไม่มี Λ เสียก่อน วิธีการจะขอกล่าวในภายหลังนะครับ ตอนนี้ขอแบบ NFA ไม่มี Λ ก่อน

การแปลงเราจะสร้างตารางที่คอลัมน์เป็นตัวอักษรที่จะเป็น input เข้ามาได้ ส่วนแถวจะเป็น state ของ FA ที่เราจะค่อยๆ สร้างขึ้นมาใหม่จาก NFA โดยชื่อ state ที่สร้างขึ้นใหม่นี้ จะเป็น set ที่ได้จากการประสมชื่อของ state จาก NFA ทำให้จำนวน state ใหม่ของ FA นี้ อาจพุ่งไปได้สูงสุดเท่ากับ 2 ยกกำลังจำนวน state ของ NFA ถ้าพูดแบบคณิตศาสตร์แล้ว state ของ FA ที่สร้างขึ้นมาใหม่ ก็คือ subset ของ set ที่มี state ทั้งหมดของ NFA และเพราะว่าเป็น set ทำให้ลำดับของชื่อ state ที่ได้จาก NFA นั้นไม่มีความสำคัญ ตัวอย่างเช่น {0, 1} เท่ากับ {1, 0}

ให้เราเริ่มจาก state แรกเริ่มต้นก่อน (ตั้งต้น) ซึ่งชื่อ state จะเป็น set ที่มีสมาชิกเป็น state เริ่มต้นจาก NFA ใส่ในแถวแรกของตาราง จากนั้นไล่แต่ละ state ใน set นั้น (กรณีนี้ มีแต่ state เริ่มต้น) ว่าตัวอักษรนั้นๆ ไปยัง state ไหนได้บ้าง ก็ให้เอา state ที่ไปได้นั้น มาเพิ่มใน set ที่เป็น state ใหม่ ที่อยู่ภายใต้ตัวอักษรนั้นๆ ขั้นต่อมา

จากนั้นจะมี set ของ state เกิดขึ้นมา ก็เอาไปใส่ในแถวถัดไป แล้วแก้โจทย์แบบเดิมต่อ จนกว่าจะไม่มี set ของ state ให้แก้โจทย์อีก

ตัวอย่างนะครับ

nfa01

ถ้าจะเขียนเป็นตารางแสดงวิธีการเปลี่ยน state ของ NFA นี้ (WordPress.com ทำตารางยังไงเนี้ย)

state  | a | b
0 | 1 | 2
1 | 1,2 | 2
2 | null | 1

null หมายถึงไม่มีที่ให้ไปต่อนะครับขั้นแรกเริ่มจาก state เริ่มต้นก่อน พบว่าอักษร a พาไป state 1 ได้ ส่วนอักษร b พาไป state 2 ได้

state  | a | b
{0} | {1} | {2}

แก้ {1} พบว่า a ไปได้ทั้ง 1 หรือ 2 ส่วน b ไปไหนไม่ได้ ในส่วนของ {2} ไป {1} ได้ ถ้าเป็นอักษร b เข้ามา

state  | a | b
{0} | {1} | {2}
{1} | {1, 2} | null
{2} | null | {1}

เราได้ตารางสำหรับ FA มาแล้ว สำหรับ state ปลายทาง

ก็จะเหลือ state {1, 2} ก็ให้ไล่เรียงในแต่ละสมาชิกของ set นี้ ซึ่งก็มีสอง state คือ {1} และ {2} ถ้าอักษรเข้ามาเป็น a แล้วเดินจาก state 1 จะไปได้ทั้งที่ state 1 หรือ state 2 และถ้าเดินจาก state 2 จะไปไหนไม่ได้ รวมแล้วจึงได้ {1, 2}

จากนั้น ไล่ตามอักษรต่อไป ถ้าอักษรเข้ามาเป็น b ถ้าเดินจาก state 1 จะไปไหนไม่ได้ และถ้าเดินจาก state 2 จะไป {1} รวมกันได้ {1}

state | a | b
{0} | {1} | {2}
{1} | {1, 2} | null
{2} | null | {1}
{1, 2} | {1, 2}| {1}

เราก็จะได้ FA สำหรับ NFA ดังกล่าว สำหรับ state ปลายทาง ของ FA (accepting state ที่ FA จะชี้ว่า string ที่เข้ามานั้น ยอมรับได้) คือ set ที่มี state ที่เป็นปลายทางของ NFA กรณีนี้ state 0 และ 1 เป็น accepting state ของ NFA ดังนั้นจาก state ทั้งหมดของ FA จะได้ว่า accepting state ของ FA คือ {0}, {1} และ {1, 2}

อ้างอิง http://www.cs.odu.edu/~toida/nerzic/390teched/regular/fa/nfa-2-dfa.html

ทดลองบีบย่อไฟล์แบบ Brotli

เคยทดลองวิธีบีบย่อไฟล์ด้วยโปรแกรม Zopfli มาก่อน ตอนนี้มีรูปแบบใหม่จาก Google มาอีก คือ Brotli ซึ่งใช้ไฟล์รูปแบบใหม่ไม่เหมือน deflat เลยจะเปิดด้วย deflate ไม่ได้ ไหนๆ เคยทดลอง Zopfli มาแล้ว ลองกับ Brotli บ้างจะเป็นไร สร้างโปรแกรมนี้จาก git โดยพลันครับ

git clone https://github.com/google/brotli.git
cd brotli/tools
make -j4

จะได้ไฟล์โปรแกรม bro มาใช้ ก็ทดสอบคร่าวๆ เหมือนคราวก่อน โดยสั่ง time ไว้ด้วย

time ./bro --quality 11 --input enwik8 --output enwik8.bro --verbose
Brotli compression speed: 0.159104 MB/s

real    10m2.654s
user    9m54.819s
sys    0m4.588s

ได้ไฟล์บีบแล้วชื่อ enwik8.bro ขนาด 27,721,194 ไบต์ บีบมาจากไฟล์ enwik8 ขนาด 100,000,000 ไบต์

ถ้าพิมพ์คำสั่ง bro เฉยๆ มันจะรอรับข้อมูลจาก stdin ซึ่งมักจะเป็น keyboard นะครับ
ผ่านทาง http://www.cnx-software.com/2015/09/24/brotli-compression-algorithm-combines-high-compression-ratio-and-fast-decompression/

ใช้งาน Codebender เขียนโปรแกรมลง Arduino ผ่านเว็บได้เลย

เคยลอง codebender แบบสั้นๆ ไปครั้งหนึ่ง แต่ตอนนั้นบรรยายภาษาอังกฤษ คราวนี้บรรยายไทยบ้าง โดย codebender เป็นเว็บที่ใช้เขียนโปรแกรม Arduino sketch บนหน้าเว็บแล้วส่งขึ้น Arduino ทางหน้าเว็บได้เลย มีปุ่มที่ทำงานได้เหมือน Arduino IDE มาก ส่วน library ที่มีมาให้ก็เยอะน่าจะครบกับหลายๆ งาน โดยไม่ต้องติดตั้งเอง

การใช้งานก็ไปที่เว็บ codebender เพื่อสมัครและพิมพ์ (เขียน) โปรแกรมตามต้องการ หรือใช้โปรแกรมของคนอื่น เช่น https://codebender.cc/sketch:108498 และอาจต้องทำการติดตั้ง plug-in บน browser เพื่อให้ browser นั้นใช้งาน serial port ได้ โดยจะมี link ให้กดครับ หลังจากนั้นก็เลือกแบบของ Arduino (ของผมเป็น Leonardo) และ serial port (ของผมเป็น COM7) ให้ถูกตัว แล้วกด Run on Arduino

ถ้า log in เข้าไป จะมีให้เลือก programmer ได้ด้วย หรืออาจไปที่หน้า https://codebender.cc/how_it_works โดยไม่ต้อง log in แต่ต้องกด next ไปเรื่อยๆ เพื่อให้จบการสาธิต

ถ้าใช้ Linux อาจต้องการสิทธิ์ในการเข้าถึง serial port ด้วยคำสั่ง gpasswd ดูวิธีการได้ที่ http://feedback.codebender.cc/knowledgebase/articles/388068-using-arduino-on-linux