จัดการ Data Science Process ง่ายๆด้วย Data Science Version Control (DVC)

ใครที่อยู่ในสายงาน Data Science น่าจะเคยเจอปัญหาเกี่ยวกับการจัดการข้อมูลและการทดลองกันมาบ้างไม่มากก็น้อย โดยระดับความ “พัง” ของปัญหาที่ว่าก็มีหลากหลายตามแต่บุญแต่กรรมของแต่ละคน ในที่นี้ก็พอจะยกตัวอย่าง case ยอดนิยมได้เป็น:

  • Preprocess data แล้วได้ผลลัพธ์ไม่เหมือนเดิม — เคสนี้ดีหน่อย เพราะยังพอไปไล่ script ได้ว่าผิดตรงไหน แต่ก็จะเสียเวลาและเสียอารมณ์ไปนิดนึง
  • จำไม่ได้ว่า model ที่เวิร์คมันตัวไหน — อันนี้จะยกระดับความพังขึ้นมาหน่อย เพราะต้องไปไล่อ่าน report ที่เคยทำไว้ (ซึ่งในความเป็นจริงคือ… ใครเค้าทำ report กัน!) หรือแย่สุดก็เอา model weight มาลองทีละอัน มันต้องมีซักอันแหละที่ใช้ได้
  • Reproduce ผลลัพธ์ไม่ได้ — อันนี้นี่เรียกว่าพังขั้นสุด เพราะแปลว่า ​model ที่เรามีแทบจะใช้ไม่ได้เลยทีเดียว หยาดเหงื่อและแรงงานที่ใส่ลงไปนี่แทบเป็นศูนย์

ซึ่งปัญหาทำนองนี้ถือเป็นปัญหาใหญ่ในวงการถึงขนาด Google ต้องตีพิมพ์ paper เพื่อเตือนถึงความเสี่ยงในการพัฒนา Machine Learning Application และ การหลีกเลี่ยงปัญหาที่จะตามมาในอนาคตขึ้นมาเลยทีเดียว

ทีม Data Scientist ที่ Datawow เองก็เจอปัญหาแบบนี้เช่นกัน เพราะด้วยธรรมชาติของงานที่ต้องทำเป็น iteration และมีรายละเอียดเล็กๆ น้อยๆ ที่สามารถส่งผลกับ outcome เป็นอย่างมาก ซึ่งเราก็พยายามหาเครื่องมือต่างๆมาช่วยแก้ปัญหานี้ จนเราไปเจอเครื่องมือที่ชื่อว่า Data Science Version Control และพบว่าเจ้าเครื่องมือนี้ช่วยให้เราทำงานได้ราบรื่นขึ้นมาก เลยถือโอกาส share ไว้เผื่อจะเป็นประโยชน์ไม่มากก็น้อยครับ

อะไรคือ Data Version Control (DVC)

DVC คือ ​Versioning Tool สำหรับ data science process ในแบบเดียวกับที่ Git ถูกสร้างมาเพื่อจัดการ Code Versioning ยังไงจะขอ assume ว่าทุกคนรู้จัก และเข้าใจการทำงานของ Git แล้วนะครับ

ข้อจำกัดของ Git คือตัวมันเองไม่ได้ถูกออกแบบมาให้ scale ได้ดีเมื่อต้องจัดการกับ repository ใหญ่ๆ [1] ซึ่งเป็นเหตุการณ์ที่มักจะเกิดขึ้นในงานด้าน data science ที่ dataset หรือ model weights มักจะมีขนาดใหญ่มากเมื่อเทียบกับ code ที่เกี่ยวข้อง และ DVC ถูกสร้างมาเพื่อแก้ปัญหาในจุดนี้

สิ่งที่ DVC ทำคือ มันจะเช็คว่า data ที่เราต้องการติดตามมีอะไรบ้าง โดยดูจาก hash ของ data ชุดนั้นๆ หลังจากนั้นจะทำการสร้าง reference file ของ state นั้นๆ ของ repository ไว้ ซึ่งตัว reference file จะเป็น text file ที่ระบุ dependency และ output ของ state นั้นไว้ โดยปกติ reference file นี้จะมีขนาดใกล้เคียงกับ code โดยทั่วไป ซึ่งเราสามารถใช้ Git จัดการได้อย่างง่ายดาย

และจุดเด่นอีกอย่างนึงของ DVC คือมันจะคอย track data processing pipeline ให้เราในรูปแบบของ Directed Acyclic Graph (DAG) เหมือนอย่างที่ Airflow ทำ โดยที่เราไม่ต้องมี server หรือ app เพิ่มเติมจากเดิมนอกจากตัว DVC เลยครับ

DVC workflow source: https://dvc.org/static/img/flow.gif

เพื่อให้เห็นภาพมากขึ้น เรามาดูตัวอย่างการใช้งานกันดีกว่าครับ ในตัวอย่างนี้ผมเอามาจาก tutorial ของ DVC ซึ่งเริ่มจากการ extract data ไปจนถึงการเปรียบเทียบ model ในแต่ละการทดลอง งั้นเรามาเริ่มกันเลยดีกว่าครับ

ก่อนอื่นเราจะต้องติดตั้ง DVC ลงบนเครื่องของเราก่อนนะครับ โดยในที่นี้เราจะติดตั้งด้วยคำสั่ง

pip install dvc

จากนั้นเราจะไปยัง project reporsitory ที่เราจะทำงานกันนะครับ ซึ่งในตัวอย่างนี้จะชื่อว่า dvc-exampleโดยมี project structure ดังนี้ครับ

dvc-example
├── data
│   └── Posts.xml.tgz
├── references
└── scripts
    ├── evaluate.py
    ├── featurization.py
    ├── split_train_test.py
    ├── train_model.py
    └── xml_to_tsv.py

เนื่องจาก DVC เป็น tool ที่ถูกสร้างมาให้ใช้คู่ไปกับ Version Control ดังนั้นก่อนที่เราจะใช้งาน เราควรจะแน่ใจว่าเราได้มี version control เรียบร้อยแล้ว โดยในที่นี้เราจะใช้ git ครับ

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

dvc init

ซึ่ง DVC จะทำการสร้าง folder .cache ขึ้นมาเพื่อไว้เก็บ reference file อย่างที่ได้เล่าไปข้างต้นและสร้าง .gitignore ไว้ให้โดยอัตโนมัติ เพียงเท่านี้ repository ของเราก็พร้อมที่จะให้งาน DVC แล้วครับ โดยเราสามารถเพิ่ม data เข้าไปได้ด้วยคำสั่ง DVC จะทำการเช็ค content ของ Posts.xml.tgz และแปลงเป็น md5 และเก็บไว้ใน folder .cache และสร้าง file ที่ชื่อว่า Posts.xml.dvc เพื่อเป็น reference ไปยัง data ที่ cache ไว้ครับ

ผลลัพธ์ของคำสั่ง dvc add data/Posts.xml.tgz

Posts.xml.tgz.dvc จะเก็บ md5 hashes ของ data ที่เราเพิ่มไว้ใน dvc เป็น reference เมื่อต้องการนำ data ไปใช้

หลังจากที่ได้เพิ่ม data เข้าไปใน DVC แล้ว ใน repository ของเราจะมี data อยู่ 2 copy ครับ และเวลาที่เราจะใช้งาน data ของเรา dvc จะทำการเช็คว่า data ที่เรามีตรงกับ cache ที่ dvc บันทึกไว้รึเปล่า

ถัดมาคือ เราจะมาเพิ่ม reference file ทั้งที่ dvc สร้างขึ้นไปใน git ของเรา แต่ก่อนอื่นเรามาดูกันว่า git เห็น repository ของเราเป็นยังไงบ้าง

จะเห็นว่ามีแค่ .dvc file และ .gitignore เท่านั้นที่ถูกเพิ่มไปใน git และเมื่อเราต้องการใช้ data DVC จะไปหา file ตามที่ระบุไว้ใน .dvc file ที่นี้สิ่งที่เราต้องทำก็เหลือแค่ commit code และ .dvc file ไปบน remote git repository ที่ทีมเราใช้งานอยู่ครับ (เช่น github)

dvc add จะเพิ่ม reference file และ .gitignore ของ data ไปใน git

อ่านมาถึงตรงนี้อาจจะสงสัยว่า เอ๊ะ มันจะแก้ปัญหาเรื่อง data ของ Git ได้ยังไง? เพราะแบบนี้ Data ก็ยังอยู่แค่บนเครื่องของเรานี่หน่า?

จากขั้นตอนที่เล่ามา ตัว DVC เองทำหน้าที่เป็นเหมือนอีก Layer นึงของ Git โดย layer นี้จะใช้เพื่อจัดการข้อมูลอย่างเดียว ไม่เกี่ยวกับ functional ต่างๆ และใช้ .dvc file เป็นตัวเชื่อมต่อของ 2 layers เข้าด้วยกัน ในส่วนของ dvc นั้นมีคำสั่งที่ชื่อว่า dvc remote ไว้สำหรับเพิ่ม remote repositoy เพื่อเก็บและแชร์ data โดยปัจจุบันรองรับ file storage ยอดฮิต ทั้ง AWS S3, Azure Blob Storage, และ Google Cloud Storage, HDFS หรือจะเป็น Local Server ก็ได้ครับ ตัวอย่างนี้ผมจะใช้ S3 เป็น remote repository ของผม ซึ่งการ config ก็ง่ายมากครับ แค่ใช้คำสั่ง

dvc remote add -d myrepo s3://yous3bucket/

หลังจากที่เพิ่ม remote repository แล้ว เราสามารถ dvc push เพื่อนำ file ไปไว้ที่ remote repository ได้

dvc push จะนำ Data Cache ที่เราทำไว้ไปวางไว้ที่ Remote Repository เหมือนเวลาเราสั่ง git push

Remote Repository จะมีแค่ data cache เท่านั้น การใช้งานก็สามารถ dvc pull ได้เลย

ถ้าเราต้องการ share data ระหว่างทีม เราแค่ทำตาม step ต่อไปนี้

  • setup DVC และ config remote repository ให้เรียบร้อย
  • git pull หรือ git clone repository ที่มี code และ dvc file
  • dvc pull

สิ่งที่ทีมเราจะได้คือ project ใน state เดียวกับที่เราเก็บไว้บน git ครับ

สมาชิกในทีมสามารถ git pull และ dvc pull ได้อย่างง่ายดาย

ถัดมาเราจะมาดู feature ที่ผมคิดว่าเจ๋งสุดๆ ของ DVC กันครับ นั่นคือ การจัดการ data pipeline ด้วย DAG กันครับ

เราสามารถสั่งให้ dvc บันทึกการเปลี่ยนแปลงที่เราทำกับ data ของเราได้ เช่น ถ้าผมต้องการ unzipPosts.xml.tgz ผมทำได้ด้วย

**dvc run** -d data/Posts.xml.tgz \               # input data
        -o data/Posts.xml \                   # output data
        -f extract.dvc \                      # reference file path
        **tar** -xvf data/Posts.xml.tgz -C data   # command to run

สิ่งที่ dvc ทำคือจะนำ input และ output data มาทำ hashes และเก็บไว้ใน .cache เหมือนเวลาเราเพิ่ม data ใหม่เข้าไป และสร้าง reference file ไว้ตาม path ที่ระบุใน parameter-f อย่างในกรณีนี้เราก็จะได้ file reference/extract.dvc ซึ่งจะเก็บข้อมูลดังนี้

extract.dvc บันทึกคำสั่ง, dependencies, และ outputs ที่เกี่ยวข้องใน state นั้นๆไว้

หรือในกรณีที่มี input หรือ output มากกว่า 1 อันเราก็สามารถทำได้แบบนี้ครับ

dvc run -d code/evaluate.py \
        -d data/model.pkl \
        -d data/matrix-test.pkl \
        -M auc.metric \
        -f evaluate.dvc \
        python code/evaluate.py data/model.pkl \
               data/matrix-test.pkl auc.metric

นคำสั่งนี้คือการทำ Model Evaluation ด้วย code/evaluate.py ซึ่งรับ model weight model.pklและ data matrix_test.pkl และสร้าง auc.metric ออกมา เพื่อใช้วัดผลโมเดลของเรา และการที่เพิ่ม parameter -m auc.metricเพราะผมต้องการให้ dvc เก็บผลลัพธ์ของแต่ละ model ไว้เพื่อใช้ในอนาคตครับ

และลองสังเกตบรรทัดสุดท้ายดูนะครับ จริงๆแล้วมันคือคำสั่งใน terminal ธรรมดาเลย ซึ่งผมสรุปเอาเองว่า dvc น่าจะสามารถใช้ได้กับทุกภาษาที่ run ผ่าน terminal ได้ แต่อันนี้ผมก็ยังไม่เคยลองนะครับ ถ้าใครลองแล้วได้ผลยังไงก็ลอง share ได้ครับ

อาจจะมีคนสงสัยว่าตัว parameter ที่ใส่ใน dvc run มีจำกัดจำนวนรึเปล่า ส่วนตัวผมยังไม่เคยลองครับ แต่ผมคิดว่า ถ้าเราต้องใส่ input จำนวนมากเกินไปต่อ 1 process อาจจะบอกว่า process นั้นใหญ่เกินไป ซึ่งการแบ่งออกมาเป็น process ย่อยๆอาจจะเป็นทางเลือกที่ดีกว่าครับ

ทีนี้เราก็สามารถ run data pipeline ของเราได้ตามปกติด้วย pattern ที่เล่าไปด้านบนครับ

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

dvc pipeline show --ascii evaluate.dvc

จากรูปจะเห็น pipeline ทั้งหมดที่เราใช้ครับ ให้มองว่า *** คือ data ของเราครับ สังเกตดูหลังจากการ featurization จะเห็นว่าเรามี data 2 สาย เนื่องจากเรา split อกกมาเพื่อ train model ของเราครับ

ในกรณีที่เรา sahre data กับคนอื่นๆในทีมและอยากดูย้อนกลับไปได้ว่า เราได้ทำอะไรกับ data เราหรือคนอื่นในทีมได้ทำอะไรไปแล้วบ้าง เราสามารถ verify ผลลัพธ์ที่เราเห็นได้โดยไม่ต้อง run แต่ละ step เอง แค่เราสั่ง

dvc repro evaluate.dvc

dvc จะทำการเช็ค dependencied และ execute คำสั่งต่างๆที่ระบุไว้ใน evaluate.dvc และแสดงผลลัพธ์ออกมาให้เราเหมือนเรา run คำสั่งทั้งหมดครับ

นอกจากจะช่วยให้เราติดตาม actions ที่เราทำกับ data ของเราได้แล้ว dvc ยังช่วยให้เราใช้ git branch ในการจัดการจัดการ experiments ต่างๆได้ในแบบเดียวกับที่ใช้จัดการ features ใน software development แบบรูปด้านล่างครับ

การ reproduce pipepine หลังจากการเปลี่ยน model parameters

อย่างในที่นี้ผลแก้ feature ของ model จาก unigram เป็น bigram และเปลี่ยน dimension จาก 5,000 เป็น 500 ซึ่งจะมีผลกระทบตั้งแต่ featurize.py ลงมา ซึ่ง dvc เองก็ฉลาดพอที่จะ run เฉพาะส่วนที่มีการเปลี่ยนแปลง ไม่ได่ run ตั้งแต่ตอน extract ข้อมูลจากPosts.xml.tgz ครับ

และถ้ายังจำ auc.metric ที่ผมเขียนไว้ก่อนหน้านี้ได้ dvc สามารถแสดง metric เดียวกันใน branch ที่แตกต่างกันได้ ซึ่งจุดนี้ทำให้เราสามารถเปรียบเทียบการทดลองหลายๆอันได้ครับด้วยคำสั่ง dvc metrics show -a ครับ

auc.metric ที่เรา track ไว้ตอนแรกทำให้เราเปรียบเทียบระหว่างการทดลองหลายๆอันได้

นี่ก็คือทั้งหมดที่อยาก share เกี่ยวกับเครื่องมือสำหรับจัดการ data science pipeline ที่ผมคิดว่าใช้งานง่ายและแทบจะไม่มีต้นทุนในการเริ่มใช้งานเลยครับ

นอกจาก features ของ dvc แล้ว สิ่งที่ผมชอบอีกอย่างเกี่ยวกับ dvc คือทีมพัฒนามีความ active และ open minded มากๆในระดับที่แทบจะตอบ issue และ release update แทบทุกวันเลยครับ

สุดท้ายขอขอบคุณที่ติดตามอ่านมาจนจบนะครับ ถ้ามี comment หรือ คำแนะนำตรงไหนก็ยินดีเลยครับ :)

References:

[1] http://osdir.com/ml/git/2009-05/msg00051.html

Have a question?

Drop us a line and we will get back to you