วิธีรัน Jupyter Notebook, ปิด browser แล้วระบบส่ง LINE บอกพร้อมภาพ เมื่อเสร็จแล้ว ค่อยกลับมาดูผล

ปัจจุบัน งานด้าน Data Science ก็มักจะใช้ Jupyter Notebook เพราะสะดวกในการทดลอง ทดสอบ ทำทีละบรรทัด ดูผล ปรับแต่งไปได้เรื่อย ๆ แถมสามารถซื้อ Server ส่วนกลาง ลงทุน GPU แล้วใช้พร้อม ๆ กันหลาย ๆ คนทั้งทีมก็ได้

ปัญหาอยู่ตรงที่ การสร้าง Model มักจะใช้เวลานาน (มาก) แล้ว Jupyter มันเป็น Web-based จะปิด Browser ก็ได้ แต่หลายคนคงเคยเจอว่า พอกลับมาเปิด URL เดิม ก็ไม่เห็นผลที่รันแล้ว

จริง ๆ แล้วคือ Jupyter Notebook นั้น ทำงานต่อไป จนเสร็จแล้วแหล่ะ แต่ เว็บ Browser น่าจะไม่สามารถต่อ Session ได้ (หวังว่าจะนึกภาพออก) ที่สำคัญ มันรันนานมาก เสร็จเมื่อไหร่ก็ไม่รู้ แล้วจะรู้ได้อย่างไรว่าเสร็จ เสร็จแล้วผลเป็นอย่างไร

ในบทความนี้ จะนำเสนอวิธีการที่ทำให้

  1. สั่งรันงานที่ใช้เวลานาน (ขอยกตัวอย่าง 100 วินาที) แล้วปิด Browser ไปทำอย่างอื่นต่อได้เลย
  2. เมื่อระบบทำงานเสร็จ จะแจ้ง LINE Notify พร้อมแนบภาพ Graph ผลลัพธ์ ส่งมาด้วย
  3. พอกลับมาเปิด Browser กลับมาใน Jupyter Notebook เดิม สามารถดูผลการรันอื่น ๆ ได้อีกครั้งเหมือนกับไม่ได้ปิด Browser
ภาพรวม

เริ่มต้นจาก Import

import sys
from IPython.display import clear_output
import logging
import sys
import requests
import datetime,time,pytz
import random

Function ในการส่ง LINE Notify

เป็นส่วนของ LINE TOKEN และ function “jobdone” ไว้ส่ง LINE Notify สามารถกำหนดข้อความ และกำหนดภาพที่จะแนบได้

LINE_TOKEN="YOUR-LINE-TOKEN"

def jobdone(LINE_TOKEN, message="Done!", img=None):
    notify_url = 'https://notify-api.line.me/api/notify'
    header={
        'Authorization': "Bearer " + LINE_TOKEN
    }        
    data=({
        'message': message
    })

    files = {'imageFile': open(img, 'rb')} if img else None
    session = requests.Session()
    response = session.post(
        notify_url,
        headers=header,
        files=files,
        data=data
    ).json()
    if files:
        files['imageFile'].close()

Function สำหรับ Plot

รับข้อมูล และ สร้างภาพเพื่อประกอบการส่ง Line

import matplotlib.pyplot as plt
def plot_something(data, resultfilename):
    fig, ax = plt.subplots()
    ax.plot(data)
    ax.grid()
    fig.savefig(resultfilename)
    plt.show()

ส่วนที่ใช้เวลานาน

%%capture capture_output
resultfilename="result.png"
start_time=time.time()

data=[]
for i in range(100):
    data.append(random.random()*100)
    time.sleep(1)
plot_something(data, resultfilename)

duration=time.time()-start_time
now=datetime.datetime.now(pytz.timezone('Asia/Bangkok')).strftime("%Y-%m-%d %H:%M:%S")
jobdone(LINE_TOKEN, f'{now} เสร็จแล้วนะโว้ย [{duration:.2f} sec.]', img=resultfilename)

ตรงนี้ต้องอธิบายเพิ่ม

ใน Jupyter จะมีสิ่งที่เรียกว่า “magic cell” ในที่นี้คือ

%%capture capture_output

โดยที่ %%capture คือคำสั่งที่จะเก็บผลลัพธ์ทุกอย่าง ที่ส่งออกจาก stdout, stderr รวมถึงภาพด้วย ในคำสั่งข้างต้น จะส่งออกไปยัง capture_output ซึ่งจะนำไปใช้ในภายหลัง

ต่อมา จะจำลองการทำงานที่ยาวนาน คือ random ค่าระหว่าง 1-100 เอาไปใส่ใน array “data” จากนั้น ก็ sleep 1 วินาที รวมแล้ว ขั้นตอนนี้จะทำงาน 100 วินาที หรือ 1 นาที 40 วินาที (ในการทำงานจริง ยาวนานกว่านั้นมาก ๆ)

เมื่อเสร็จแล้ว plot_something จะเอาข้อมูล data ไป plot แล้วแสดงผล และ save เป็นไฟล์ตามที่กำหนด คือ result.png

คำสั่งต่อมา เป็นการแสดง วันเวลาปัจจุบัน โดยแสดงเป็น Timezone ของไทย

สุดท้าย function jobdone กำหนดข้อความ และภาพที่จะส่ง

Let’s go!

รอไป 1 นาที 40 วินาที ก็จะได้ LINE Notify (อ่านขั้นตอนการได้มาซึ่ง LINE Token ได้จาก วิธีแจ้งเตือนจาก Google Forms เข้า LINE )

กลับมาใน Jupyter Notebook

ใน cell ใหม่ ใช้คำสั่ง

capture_output()

ก็จะแสดงผลสิ่งที่ดำเนินการไป ระหว่างนั้นได้ครับ

หวังว่าจะเป็นประโยชน์ครับ