Author: Sira Ekabut

  • สอนใช้ Python ใน Excel ตอนที่ 2  : List, Loop, Condition

    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition

    หลังจากในตอนที่แล้วผมได้มีการแนะนำแนวทางการใช้ Python ใน Excel เบื้องต้นไปแล้ว ในตอนนี้หลักๆ แล้ว ผมจะมาแนะนำวิธีเขียน Code เพื่อสร้าง Loop ใน Python ให้เพื่อนๆ ได้รู้จักกันครับ

    Loop คือการสั่งให้โปรแกรมคอมพิวเตอร์ทำงานซ้ำๆ ตามที่เรากำหนด ซึ่งใน python มี Loop แบบนึงที่นิยมมาก คือ for loop ซึ่งค่อนข้างทรงพลังมาก

    วิธีการคือ for loop จะวนพิจารณาทำงานซ้ำโดยอ่านค่า Item ใน Object ที่มี item ย่อยๆ อยู่ข้างใน ที่ใช้บ่อยมากคือ List Range กับ String (ข้อความ) เป็นต้น ซึ่งเป็นเรื่องที่ผมจะอธิบายในบทความนี้

    แต่ก่อนที่ผมจะอธิบายเรื่อง Loop เพื่อให้เห็นภาพมากขึ้น เรามาทำความรู้จัก List ให้ดีขึ้นกันอีกนิดนึงดีกว่าครับ

    เรื่องของ List ใน Python

    List คือประเภทข้อมูลแบบนึงของ Python ที่สามารถเก็บข้อมูลไว้ในตัวมันได้หลายตัว โดยที่ข้อมูลแต่ละตัวจะเป็นข้อมูลประเภทอะไรก็ได้ (ใน List มี List ซ้อนอีกก็ได้) เก็บซ้ำกันก็ได้ และลำดับการเก็บข้อมูลมีความหมาย (เราอ้างอิงตำแหน่งของมันได้)

    สมมติว่า List ต้นฉบับคือแบบนี้

    colorList=["red","green","blue","yellow"]
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 1

    อ้างอิงถึง item ใน List

    หากเราต้องการอ้างอิงถึง item ใน List สามารถใช้

    ListName[เลขindex]

    โดยที่เราสามารถอ้างอิงเลข index ได้ 2 แบบ คือ

    • เลข index เป็นบวก โดยเริ่มจาก item แรก คือ index 0 คือ แล้ว item 2 คือ index 1
    • เลข index ติดลบ ไล่ย้อนหลับนับ item สุดท้ายเป็น -1, รองสุดท้ายคือ -2

    เช่น

    • colorList[1] จะได้ item ที่ 2 คือ string ว่า green
    • colorList[-3] คือ item ลำดับที่ 3 จากท้าย ซึ่งในที่นี้ก็คือ green เช่นกัน
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 2

    Slice หั่นเลือกเอาช่วง item ใน List

    เราสามารถ slice เลือกบางส่วนของ List ได้ด้วยวิธีแบบนี้

    ListName[ indexเริ่ม : แต่ไม่ถึง indexจบ ]
    colorList[1:3]
    # เอาตั้งแต่ index1 (เริ่มตัวที่2) แต่ไม่ถึง index3 (ไม่ถึง 4 คือเอาถึงตัวที่3)

    สรุปแล้ว [1:3] แปลว่าเอาตั้งแต่ ตัวที่2 ถึงตัวที่ 3

    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 3

    ถ้าจะเริ่มเอาแต่แรก สามารถใส่ colorList[0:3] หรือจะเว้นไปเลย เหลือแค่ colorList[:3] ก็ได้

    colorList[:3]
    # เอาตั้งแต่แรก แต่ไม่ถึง index3 (ไม่ถึง 4 คือเอาถึงตัวที่3)

    สรุปแล้ว [:3] แปลว่าเอาตั้งแต่ ตัวแรก ถึงตัวที่ 3

    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 4

    ถ้าจะเอาถึงตัวสุดท้าย ก็เว้นการใส่ตัวสุดท้ายไปเลยก็ได้ เช่น

    colorList[1:]
    # เอาตั้งแต่ index1 (เริ่มตัวที่2) จนถึงตัวสุดท้าย

    สรุปแล้ว [1:] แปลว่าเอาตั้งแต่ตัวที่2 ถึง ตัวสุดท้าย

    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 5

    แล้วก็สามารถใส่ index ติดลบได้เช่นเดิม

    colorList[1:-1]
    # เอาตั้งแต่ index1 (เริ่มตัวที่2) แต่ไม่ถึง index -1 (ไม่ถึงตัวสุดท้าย คือเอาถึงตัวรองสุดท้าย)

    สรุปแล้ว [1:-1] แปลว่าเอาตั้งแต่ตัวที่2 ถึง ตัวรองสุดท้าย

    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 6

    การรวม List เข้าด้วยกัน

    เราสามารถรวม Item ใน List เข้าด้วยกันได้ด้วยเครื่องหมาย + ธรรมดาเลย simple สุดๆ เช่น

    MyList1=["red","green","blue"]
    MyList2=[55,20,200]
    combinedList=MyList1+MyList2
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 7

    แต่ถ้าจะมัดรวมเป็นคู่อันดับ ให้ใช้ zip มาช่วย เช่น

    MyList1=["red","green","blue"]
    MyList2=[55,20,200]
    zipObject=zip(MyList1,MyList2)
    zipList=list (zipObject)
    print(zipList)
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 8

    เริ่มทำการวน Loop

    พอเราเริ่มรู้จัก List ที่เป็น Object ประเภทนึงที่สามารถเก็บข้อมูลหลาย item ในตัวมันเองแล้ว เราลองมาเริ่มดูวิธีวน Loop กันบ้าง

    โดยโครงสร้างของ for loop ใน Python เป็นแบบนี้

    for item in Objectที่มีSequence:       # ต้องมี : ด้วย
        คำสั่งใน loop ที่จะให้ทำซ้ำ   #ต้องมี tab เพื่อย่อหน้าเข้ามา 1 ทีด้วย
        คำสั่งใน loop ที่จะให้ทำซ้ำ   #ต้องมี tab เพื่อย่อหน้าเข้ามา 1 ทีด้วย
    คำสั่งที่ไม่เกี่ยวกับ loop          #ถ้าไม่มีย่อหน้าคือไม่อยู่ใน loop

    วน Loop ใน List

    สมมติถ้าอยากจะวน Loop ใน colorList ที่สร้างไว้ แล้วพยายาม print เอาข้อมูลสีออกมาในแบบตัวพิมพ์ใหญ่

    โดยผมลองเขียนให้ดู เพื่อทำความเข้าใจแบบละเอียด ดังนี้

    เราสามารถจะตั้งชื่อตัวแปรที่เอาไว้วน Loop (ตัวที่อยู่หลัง for) ว่าอะไรก็ได้ เช่น i, x, num, char, color, blahblah หรืออะไรก็ได้

    ซึ่งในที่นี้เราตั้งว่า color โดยการวน loop แต่ละรอบ ตัวแปร color นี้ก็จะเปลี่ยนไปเรื่อยๆ โดยรับค่าที่เป็นข้อความ “red” “green” “blue” “yellow” เปลี่ยนไปเรื่อยๆ ทีละตัว

    การที่เราจะทำให้เป็นตัวพิมพ์ใหญ่ เราสามารถใช้ method ที่ชื่อว่า .upper() มาช่วยได้

    เพราะมันคือ Method ที่ถูกฝังไว้ใน object ประเภท String หรือข้อความนั่นเอง ซึ่งชื่อสีของเราเป็นข้อความก็ต้องใช้คำสั่งนี้ได้

    for color in colorList:
        UpColor=color.upper()    #สร้างตัวแปร UpColor มาเก็บตัวพิมพ์ใหญ่
        print(UpColor)        #print UpColor ออกมาใน Console
    print("นี่คือนอก loop")

    ผลที่ได้ คือ จะมีการ print ชื่อสีแต่ละอันเป็นตัวพิมพ์ใหญ่ ออกมาใน console ซึ่งใน Excel มันจะถือว่า Console อยู่ในส่วนของ Diagnostics ด้านขวา (ลากออกมาได้นะ)

    เราจะพบว่า มีการ Print สีจำนวน 4 ครั้ง (เพราะเรามีทั้งหมด 4 สี มันไล่วน Loop Print แต่ละสี สีละ 1 ครั้ง) แต่มีการ Print คำว่า “นี่คือนอก loop” แค่ 1 ครั้ง เพราะเราใส่ไว้โดยไม่มีการย่อหน้า แสดงว่าไม่อยู่ใน loop แล้ว

    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 9

    เอาจริงๆ การวน loop print เราเขียนแค่นี้ก็พอ ไม่ต้องประกาศอะไรเยอะแยะ

    for color in colorList:
        print(color.upper())
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 10

    แต่ถ้าเราอยากให้มันออกมาใน Cell เป็น Dynamic Array เราต้องทำให้ผลลัพธ์เป็น List ก่อน Excel จึงจะเข้าใจ ถ้าปล่อยเป็น NoneType จะเอาออกมาไม่ได้

    วิธีการแบบนึงที่ทำได้คือ สร้าง List ว่างเปล่าขึ้นมาก่อน แล้ววน Loop เอา item แต่ละอันใส่ เข้าไปใน List ด้วย .append() แล้วค่อยเรียก List นั้นออกมาอีกที ดังนี้ (แล้วอย่าลืมแก้ Output ให้เป็น Excel Value ด้วย)

    UpperColorList=[]        #สร้าง UpperColorList ว่างเปล่า
    for color in colorList:
        UpperColorList.append(color.upper()) #ใส่ item เพิ่มใน List
    UpperColorList         #แสดง UpperColorList ออกมา
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 11

    List Comprehension

    อย่างไรก็ตาม เราสามารถสร้าง List ใหม่ได้ง่ายๆ กว่าที่เขียนข้างบน ด้วยเทคนิคที่เรียกว่า List Comprehension ซึ่งเหมือนการเขียน for loop แบบย่อสุดๆ ที่ให้ผลเป็น List ใหม่ได้เลย โดยไม่ต้องมานั่งเขียน code ยาวๆ เช่น

    ในรูปแบบของ

    [itemผลลัพธ์ for item in Objectที่มีSequence]

    เช่น

    [color.upper() for color in colorList]
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 12

    เดี๋ยวเราลองไปดูการวน Loop ใน item อื่นที่ไม่ใช่ List โดยตรงบ้าง

    วน Loop ใน Range

    ถ้าเราอยากจะวน Loop กับ ตัวเลข 6 ตัว ที่เริ่มจากเลข 0 เราสามารถใช้ฟังก์ชัน range(6) ได้ ซึ่งมันคือ Object ที่คล้าย List ที่ประกอบไปด้วย item 0,1,2,3,4,5 อยู่ข้างในนั่นเอง

    ลองให้มันสั่ง print เลข 0-5 ออกมาใน console โดยที่ตั้งตัวแปร i ขึ้นมาแทนแต่ละเลขขณะวน Loop

    for i in range(6):
        print(i)
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 13

    แต่ถ้าเราอยากให้มันออกมาใน Cell เป็น Dynamic Array ก็ใช้ List Compreshension ก็ได้ ง่ายดี

    [i for i in range(6)]

    เราจะได้ผลลัพธ์เป็น List ใหม่ทันที ง่ายๆ แบบนี้เลย

    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 14

    หรือถ้าอยากได้เลขที่ไม่ได้เริ่มจาก 0 ก็สามารถทำแบบนี้ได้ ในรูปแบบของ

    range(เลขเริ่ม,แต่ไม่ถึงเลขจบ)

    เช่น

    [i for i in range(3,10)]

    แบบนี้คือ 3 ถึง 9 (เลขจะไม่รวม 10)

    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 15

    และถ้าอยากจะเพิ่ม step มากกว่า 1

    range(เลขเริ่ม,แต่ไม่ถึงเลขจบ,step)

    เช่น เพิ่มทีละ 2 ก็ทำแบบนี้ได้ คือใส่ ,2 ต่อไปอีก

    [i for i in range(3,9,2)]
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 16

    กลับมาที่ range(6) คือ เลข 0-5 อีกที…

    ถ้าผลลัพธ์ที่เราต้องการไม่ได้เป็นการสร้าง List ใหม่ ก็อาจจะไม่ต้องใช้ List Comprehension ก็ได้

    เช่น ลองสร้างเลขบวกสะสมกันไปเรื่อยๆ ตั้งแต่ 0-5

    x=0
    for i in range(6):
        x=x+i
    x
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 17

    หรือเอาจริงๆ จะทำ List Comprehension เพื่อสร้าง List ผลลัพธ์ก่อน (ในทีนี้ผมเก็บไว้ในตัวแปร MyList) แล้วค่อย Sum ค่าใน List ทั้งหมดก็ได้ เช่น

    MyList=[i for i in range(6)]
    x = sum(MyList)
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 18

    วน Loop ใน String

    นอกเหนือจากการวน Loop ใน List และใน Range แล้ว เรายังสามารถวน Loop ใน String หรือข้อความได้ด้วย ซึ่งมันคือ การวนพิจารณาทีละอักขระของข้อความได้เลย ซึ่งทรงพลังมากๆ

    MyList=[]    #สร้าง List ว่างเปล่า
    for char in "thepexcel":
        MyList.append(char)    #ใส่แต่ละอักขระเพิ่มเข้าไปใน List
    MyList        #แสดง MyList ออกมา
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 19

    ซึ่งใช้ List Comprehension ง่ายกว่าเยอะ

    MyList=[char for char in "thepexcel"]
    
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 20

    การใส่ Condition

    เราสามารถจะใช้ if เพื่อสร้าง condition บางอย่างได้

    กรณีถ้าเงื่อนไขจริงแล้วให้ทำอะไรบางอย่าง (ถ้าไม่ตรงก็ไม่ทำอะไร)

    if condition:
        #action1 if true
        #action2 if true

    เช่น

    ผมทำให้ดู 2 เคสเลย ว่ากรณีเงื่อนไขเป็นจริง ก็จะทำตาม action ที่กำหนด แต่ถ้าไม่ตรงตามเงื่อนไขก็จะไม่มีอะไรเกิดขึ้น

    result1="original1"
    score=45
    if score>10:
        result1="score1 มากกว่า 10"
    
    result2="original2"
    score=5
    if score>10:
        result2="score2 มากกว่า 10"
        
    [result1,result2]
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 21

    การเขียน if แบบนี้ ซึ่งถ้าผลลัพธ์มีแค่ 1 action เราสามารถย่อได้ จะได้ไม่ต้องปวดหัวกับการ indent ย่อหน้าเข้าไป

    if condition: action_if_true

    เช่น อันนี้ผมทำเคสจริงให้ดูอย่างเดียว

    result1="original1"
    score=45
    if score>10:result1="score1 มากกว่า 10"
        
    result1
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 22

    กรณีถ้าเงื่อนไขจริงแล้วให้ทำอะไรบางอย่าง ถ้าไม่จริงให้ทำอีกอย่าง

    ก็คือนอกจากจะมี if แล้ว คราวนี้ต้องมี else เพื่อทำกรณีที่ไม่จริงด้วย ซึ่งโครงสร้างคือ

    if condition:
        #action if true
    else:
        #action if false

    เช่น

    result="original"
    score=5
    if score>10:
        result="score มากกว่า 10"
    else:
        result="score ไม่ได้มากกว่า 10"
    result
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 23

    ซึ่งถ้าแต่ละเงื่อนไขมีแค่ 1 action เราสามารถย่อได้เช่นกัน ในรูปแบบนี้ คือไม่มี : ไม่มีย่อหน้าเลยด้วย แต่จะมีการสลับเอา action กรณีจริงขึ้นมาก่อน

    action_if_true if condition else action_if_false

    แต่ action ในรูปแบบนี้จะใช้การ assign ตัวแปรไม่ได้นะ ดังนั้นผมจะใช้การ print เพื่อพิสูจน์ให้ดูแทน

    score=5
    print("score มากกว่า 10") if score>10 else print("score ไม่ได้มากกว่า 10")
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 24

    ลองใช้ condition ใน Loop

    เราลองมาดูตัวอย่างการใช้ Condition ใน Loop กัน

    สมมติว่า เราจะวน Loop ใน String เพื่อพิจารณาแต่ละอักขระ ถ้าพบว่าอักขระที่กำลังพิจารณา (char) เป็นตัว e เราจะให้ทำเป็นตัวพิมพ์ใหญ่ โดยใช้ method .upper() ภายใน condition ของ if

    MyList=[]    #สร้าง List ว่างเปล่า
    for char in "thepexcel":
        if char =="e":
            char=char.upper()    #ถ้าเป็นตัว e ให้ทำเป็นพิมพ์ใหญ่
        MyList.append(char)    #ใส่แต่ละอักขระเข้าไปใน List
    MyList        #แสดง MyList ออกมา
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 25

    แต่ถ้าในกรณีนี้เราทำด้วย List Comprehension แบบทื่อๆ เลย การเขียน condition if แบบย่อแค่ action จริง จะไม่ work ในกรณีนี้

    MyList=[char.upper() for char in "thepexcel" if char=="e" ]

    เพราะว่ามันดันเป็นการสร้าง List ใหม่ โดย “คัดเลือกเฉพาะที่ตรงกับเงื่อนไข” ที่กำหนดคือเป็นตัว e เท่านั้น (กลายเป็นได้ E 3 ตัวเฉยเลย นั่นคือ กรณี False จะไม่มีการเอามาใส่ใน List)

    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 26

    ถ้าอยากให้มีทุกตัวเท่าเดิม เราควรกำหนดทั้งกรณีทั้ง True และ False แต่โครงสร้างของ List Comprehension จะเปลี่ยนไปนิดหน่อย กลายเป็น if else ซึ่งจะมีการสลับตำแหน่ง

    MyList=[char.upper() if char=="e" else char for char in "thepexcel" ]

    แบบนี้ถึงจะ work ได้ตามต้องการครับ

    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 27

    ลองการประยุกต์

    วน Loop เพื่อแก้ชื่อคอลัมน์ของ DataFrame

    สมมติผมมี DataFrame อยู่ แล้วอยากจะได้ชื่อคอลัมน์ออกมาเป็น List ผมสามารถทำได้แบบนี้

    df = xl("IrisDataSet[#All]", headers=True)
    dfColList=list(df.columns)
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 28

    ทีนี้ผมอยากจะวน Loop เพื่อทำการแก้ชื่อคอลัมน์เฉพาะที่มีเครื่องหมาย _ ให้เป็นพิมพ์ใหญ่

    ผมทำได้ดังนี้

    NewdfColList=[ col.upper() if "_" in col else col for col in dfColList ]
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 29

    แล้วเราก็เอาชื่อคอลัมน์ใหม่ assign เข้าไปใน df เดิมก็ได้ ดังนี้

    df.columns = NewdfColList
    df
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 30

    จริงๆ ก็คือเขียนรวมกันหมดเลยใน code ของ cell เดียวก็ได้นะ เช่น

    df=xl("IrisDataSet[#All]", headers=True)
    dfColList=list(df.columns)
    NewdfColList=[ col.upper() if "_" in col else col for col in dfColList ]
    df.columns = NewdfColList
    df
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 31

    คัดเลือกให้แสดง DataFrame แค่บางคอลัมน์

    ทีนี้ในเมื่อเราจัดการ List เป็นแล้ว อาจใช้มันในการเลือก List ที่ต้องการก็ได้ เช่น ผมต้องการ Columnที่ 1,4,5 เสมอ ผมอาจใช้แบบนี้

    SelectCol=[NewdfColList[i-1] for i in [1,4,5]]
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 32

    แล้วผมค่อยเอา List รายชื่อคอลัมน์นี้ใส่เข้าไปใน DataFrame เพื่อเป็นการคัดเลือกเอาคอลัมน์ที่ต้องการได้เลย เช่น

    df[SelectCol]
    สอนใช้ Python ใน Excel ตอนที่ 2 : List, Loop, Condition 33

    ตอนต่อไป

    สำหรับตัวอย่างนี้ก็ค่อนข้างยาวแล้ว ผมขอจบเพียงเท่านี้ ในตอนต่อไปเราจะพูดถึงเรื่อง Regular Expression กันครับ ซึ่งคือเรื่องที่ Python ชนะ Excel แบบขาดลอยของจริงครับ หึหึ

  • สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก

    ในบทความนี้ผมจะสอนใช้ Python ใน Excel ตั้งแต่ต้น แบบที่คนไม่รู้จัก Python มาก่อน ก็พอจะอ่านได้ครับ และแม้ว่าผมจะเพิ่งได้ใช้ Python for Excel เมื่อวานซืนเป็นครั้งแรก (เมื่อ 8 Sep 2023 ) แต่ผมพอจะมีประสบการณ์ในการใช้ Python มาบ้างแล้ว ถึงจะไม่ได้เชี่ยวชาญ แต่ก็น่าจะพอถ่ายทอดในมุมมองของคนที่เคยใช้ทั้ง 2 โปรแกรมมาก่อนได้ครับ

    การที่ Microsoft เอา Python มาอยู่ใน Excel โดยตรง สำหรับผมแล้วเป็นเรื่องที่น่าตื่นเต้นมากๆ พอๆ กับตอนใช้ Power Query ครั้งแรกเลย เพราะมันจะเพิ่มความสามารถอย่างมหาศาลในการวิเคราะห์และนำเสนอข้อมูลและมันทำให้ผมรู้สึกกลับมาสนุกกับ “การเรียนรู้ครั้งใหม่” อันนี้ ^^

    Tips : เราสามารถให้ AI เช่น ChatGPT ช่วยเขียน Code Python ให้เราได้ เนื่องจาก Python เป็นภาษาคอมพิวเตอร์ที่ ChatGPT ถนัดที่สุด และตอบแม่นกว่าภาษาอื่นมาก (แม่นกว่าสูตร Excel เยอะ)

    วิธีการสมัครใช้ Python ใน Excel

    ณ ปัจจุบัน คุณต้องมี Excel 365 แล้วต้องกดสมัครเป็น แบบ Excel 365 Insider (กดได้ฟรีเลย ในหน้า Account น่ะ) และเลือกแบบ Beta Channel เท่านั้น จึงจะใช้ Python ใน Excel ได้ และอาจต้องรอนิดหน่อยด้วย บางคนหลังจากสมัครก็ยังใช้ไม่ได้ทันที

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 34

    หากใช้ได้แล้วจะมีเครื่องมือโผล่มาที่ Ribbon ของ Formula แบบนี้

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 35

    วิธีการเรียกใช้ Python ใน Excel

    ไปใน Cell ไหนก็ได้ แล้วเขียนสูตร =PY แล้วกด Tab โปรแกรมจะเข้าสู่ “โหมดการเขียน Code Python” ทันที (หรือกด Ctrl+Alt+Shift+P ก็ได้นะ แต่ผมชอบ =PY มากกว่า)

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 36

    การรัน Code Python

    เมื่อเขียน code python หากกด Enter เฉยๆ จะเป็นการขึ้นบรรทัดใหม่ (เพราะปกติ Code มักจะเขียนหลายบรรทัด) ถ้าอยากจะให้ Code Run ให้กด Ctrl+Enter แทน

    และการเขียน Code Python นั้น ไม่ต้องขึ้นด้วยเครื่องหมายเท่ากับ นะ เราสามารถพิมพ์ Code ที่ต้องการได้เลย เช่น

    (5+2)*10

    แล้วกด Ctrl+Enter เพื่อ Run Python ซึ่งมันจะทำการส่งข้อมูลไป Run บน Cloud นะครับ (แปลว่าต้องต่อ Internet!)

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 37

    ผลลัพธ์ของ Python ใน Excel

    ผลลัพธ์ของ Python ใน Excel จะเลือกได้ 2 แบบ คือ Python Object และ Excel Value

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 38

    โดยที่ค่า Default คือ Python Object ซึ่งจะเกือบข้อมูลหลายค่าอยู่ใน Object เดียวได้

    โดยถ้าผลลัพธ์เป็น Value ที่มีค่าเดียวจะแสดงออกมาเลย (เช่น เลข 70)

    แต่ถ้าผลลัพธ์เป็นโครงสร้างข้อมูลที่มีหลายค่า อาจยังไม่แสดงออกมา แต่จะเห็นเป็น Object เช่น list, record, DataFrame แทน

    เช่น เราสามารถสร้าง list ใน Python ได้ด้วย [ item1 , item2, item3 ]
    ป.ล. คล้ายๆ M Code ของ Power Query แต่ Power Query ใช้ { } แทนที่จะเป็น [ ]

    [5,20,"cat","สาว"]
    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 39

    เราสามารถกดเปลี่ยนให้เป็น Excel Value ได้โดยผลลัพธ์จะแสดงออกมาใน Cell ของ Excel ตามปกติ และถ้ามีหลายค่าจะออกมาเป็น Dynamic Array (ใช้ได้กับ list และ DataFrame) ดังรูป

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 40

    ป.ล. ผมใช้ฟังก์ชัน FORMULATEXT ของ Excel ในการแสดงสูตร Python ออกมา

    • ,1 ด้านท้ายคือการบอกว่าผลลัพธ์ออกมาเป็น Python Object
    • ,0 ด้านท้ายคือการบอกว่าผลลัพธ์ออกมาเป็น Excel value

    การประกาศตัวแปรใน Python

    เราสามารถประกาศตัวแปรใน Python ได้ (ประกาศได้หลายตัวไปเรื่อยๆ) ด้วยการเขียนว่า

    ตัวแปร1 = expression บางอย่าง
    ตัวแปร2 = expression บางอย่าง
    ...

    ซึ่ง python จะเอาค่าฝั่งขวา มาเก็บในตัวแปรฝั่งซ้าย

    width=5
    height=10
    area=width*height
    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 41

    และโดยปกติมันจะแสดงค่าบรรทัดสุดท้ายออกมา ทำให้ในตัวอย่างนี้ เราเลยเห็นค่าเป็น 50 ของตัวแปร area นั่นเอง

    อ้างอิงตัวแปรที่เคยประกาศแล้วใน Cell อื่น

    เราสามารถอ้างอิงตัวแปรที่ประกาศไว้ใน Cell อื่นได้นะ แต่มัน “ต้องเป็น Cell ที่อยู่ก่อนหน้า”

    โดย Python จะอ่านค่าจาก Row1 จนครบก่อน จากซ้ายไปขวาทุกคอลัมน์ แล้วค่อยอ่าน Row2 (ทุกคอลัมน์จากซ้ายไปขวาอีกที)

    ดังนั้นแบบนี้ ค่าใน B4 มันอ่านหาตัวแปร width ได้ เพราะเคยประกาศไว้ใน B2

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 42

    แต่แบบนี้ค่าใน A1 มันอ่านหา width ได้ไม่ได้ เพราะว่ามันดันเรียกค่า width ทั้งๆ ที่การประกาศยังมาไม่ถึง เพราะการประกาศทำไว้ใน B2

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 43

    และถ้าแบบนี้ มันจะอ่านค่าได้ 7 เพราะมีการประกาศตัวแปร width ซ้ำอีกทีใน Cell D2 มันเลยทับค่า width ที่เคยประกาศใน B2 ไปเลย

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 44

    วิธีการอ้างอิงข้อมูลจาก Excel เข้าไปใน Python

    เราสามารถอ้างอิง Cell Reference หรือจะ Defined Name ต่างๆ หรือแม้แต่ชื่อ Query ก็ได้

    1.อ้างอิง Cell เดี่ยวๆ

    วิธีแรก คือ จิ้มที่ Cell ที่ต้องการ เพื่ออ้างอิงค่าได้เลย มันจะขึ้นฟังก์ชัน xl ขึ้นมาเอง (ซึ่งคือ ฟังก์ชันพิเศษที่เอาไว้อ่านค่าจาก Excel) พร้อมกับ Cell Reference ของสิ่งที่เราจิ้ม เช่น

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 45

    แล้วเราสามารถ Copy สูตรลงมาเพื่อให้ Cell Reference เลื่อนได้ตามปกติเลย (หรือจะ Lock $ ก็ทำได้ตามปกติเลย)

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 46

    เรายังสามารถใช้สูตรหรือฟังก์ชัน Excel คำนวณค่าที่มาจาก Python ได้ตามปกติด้วย

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 47

    แต่เอาจริงๆ การอ้างอิง Cell เดียวดูแล้วไม่ค่อยมีประโยชน์เท่าไหร่ ไม่ค่อยมีใครเค้าทำกันหรอกครับ เราไปดูวิธีที่เข้าท่าขึ้นดีกว่า

    2.อ้างอิง Range

    Python ใน Excel จะเริ่มมีประโยชน์ขึ้น ถ้าเราอ้างอิงข้อมูลเป็น Range เข้าไปให้มัน

    ตัวอย่างเช่น ถ้าเดิมทีผมมีแค่ สินค้า Unit Price และ Qty ผมสามารถอ้างอิง Range ทั้งยวงได้เลย (B2:D5) แบบนี้ (รวมหัวตาราง) มันจะฉลาดพอที่จะใส่ว่าข้อมูลเรามีหัวตารางอยู่ด้วย (headers= True)

    ถ้ามีการใส่ข้อมูลเป็นชุดเข้าไปหลายตัว ผลลัพธ์จะออกมาเป็นสิ่งที่เรียกว่า DataFrame (เป็น Object แบบนึงของ Pandas Library ของ Python ซึ่งให้มองว่าคล้ายๆ Array หรือ Table ของ Excel ก็ได้ ซึ่งเป็น Object ที่ดีมากๆ)

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 48

    3.อ้างอิง Dynamic Array

    สมมติเรามี Dynamic Array อยู่ เราก็ใช้วิธีการจิ้มที่ช่องซ้ายบน แล้วใช้เครื่องหมาย # เข้าไปหลัง Cell Reference นั้นๆ ตามปกติได้เลย

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 49

    4.อ้างอิง Table

    เราสามารถเลือก Table ทั้งอันเข้าไปใน DataFrame ก็ได้ เลือกให้ได้หัวตารางด้วยนะ ซึ่งดีกว่า Range ตรงที่ Table มันงอกออกมารองรับข้อมูลใหม่เองได้

    สูตรใน Python มันจะออกมาประมาณนี้ แบบนี้จะได้ทั้งตาราง รวมหัวตารางด้วย

    xl("TableName[#All]", headers=True)
    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 50

    5.เอาผลลัพธ์จาก Power Query

    กรณีจะเอาข้อมูลจาก Power Query เข้าสู่ Python ณ ตอนนี้ จะต้องเป็นการ Get Data จากแหล่งอื่นที่ไม่ใช่ CurrentWorkbook นะครับ เช่น ผมเอาจาก csv ในเว็บอันนี้

    https://raw.githubusercontent.com/ThepExcel/YouTube/main/SampleData.csv

    ลอง Get Data from Web แล้วใส่ url เข้าไป แล้วให้ Load to ออกมาเป็น Connection Only

    เวลาเราเรียกใช้ใน Python สามารถพิมพ์ว่า xl(” แล้วมันจะมีชื่อ Query ทั้งหมดเด้งขึ้นมาให้เลือกเลย

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 51

    พอเลือกปุ๊ปเราก็ได้ข้อมูลนั้นเข้า DataFrame จบ

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 52

    ลองเล่นกับ Python DataFrame

    ซึ่งพอสิ่งที่เราได้มันเป็น DataFrame เราสามารถจัดการมันได้หลากหลายมากขึ้นเยอะมาก เช่น

    ทำการสรุปเบื้องต้น

    DataFrome นั้นมีสิ่งที่เรียกว่า Method (คือฟังก์ชันสำเร็จรูปที่ติดมากับ Object ประเภทนั้นๆ) ที่ชื่อว่า describe ซึ่งสามารถสรุปข้อมูลจากคอลัมน์ที่เป็นตัวเลขทั้งหมดได้แบบอัตโนมัติสบายๆ เลย ซึ่งดีมาก เช่น

    df=xl("B2") #อ้างอิง Data Frame จากอีกช่องก็ได้นะ
    df.describe() #สรุปผล

    ป.ล. เราสามารถ Comment code ใน python ได้ด้วยเครื่องหมาย # นะครับ

    ถ้าอยากให้ผลลัพธ์ออกมาใน Cell ไปเลย เราต้องเปลี่ยนวิธีแสดงผลลัพธ์จาก Python Object เป็น Excel Value

    ผลลัพธ์ก็จะแสดงออกมาเป็น Dynamic Array ใน Excel เลย

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 53

    การดูรายชื่อคอลัมน์ทั้งหมด

    ใช้คำสั่งประมาณนี้ก็ได้ เพื่อเอารายชื่อคอลัมน์ออกมาเป็น list

    list(df.columns)
    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 54

    การคัดเลือกเอาเฉพาะบางคอลัมน์

    สามารถทำได้โดยการใช้คำสั่ง

    DataFrameName[ List Column Name ] เช่น

    df[["OrderID","สินค้า","ยอดขาย"]]

    จะได้แบบนี้

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 55

    ถ้าเราแค่เรียก df[[“OrderID”,”สินค้า”,”ยอดขาย”]] มาดู โดยไม่ได้มีการ assign ค่ากลับเข้าไปในตัวแปร df อันเดิม ก็จะไม่ได้ทำให้ df เดิมมีการเปลี่ยนแปลงแต่อย่างใด

    ยกเว้นการเพิ่มคอลัมน์ที่จะเห็นถัดไป ว่ามันเพิ่มเข้าของเดิมจริงๆ

    ทำการ Add Column เพิ่ม

    ผมสามารถทำการสร้างคอลัมน์เพิ่ม ในรูปแบบของ

    df["Col Name"] = สูตร

    ดังนั้นสูตรจะเป็นแบบนี้

    df["ยอดขายลด10%"]=df["ยอดขาย"]*0.9 #สร้างคอลัมน์ใหม่
    df  #เรียกตัวแปร df ที่เก็บ dataframe ออกมาดู
    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 56

    ลองดูรายชื่อคอลัมน์อีกที จะเห็นว่าใน df มีคอลัมน์ใหม่เพิ่มมาแล้วจริงๆ นะ

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 57

    ทำการ Filter DataFrame

    การ Filter DataFrame นั้นทำได้หลายแบบมากๆ แต่ผมจะใช้วิธีสร้าง Series True False (หลักการคล้ายฟังก์ชัน FILTER)

    ปกติแล้วถ้าเราอ้างอิงคอลัมน์เดียวใน DataFrame มันจะได้สิ่งที่เรียกว่า Series เช่น

    ซึ่งจะเป็นชุดข้อมูลที่มี Index อยู่ด้านซ้าย เช่น

    df["วิธีการชำระเงิน"]
    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 58

    ซึ่งเราสามารถใช้ Series มาคำนวณ หรือจะใช้ร่วมกับการเปรียบเทียบ เพื่อสร้าง Series ที่มีค่า True False ได้ เช่น

    สมมติว่าผมสนใจเฉพาะข้อมูลที่วิธีการชำระเงินเป็นเงินสด ก็ทำได้ดังนี้

    df["วิธีการชำระเงิน"]=="เงินสด"

    (ใน python ใช้ == เป็นการเปรียบเทียบนะ ไม่ใช่แค่ =)

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 59

    แล้วถ้าผมเอา DataFrame Series True False นี้ไปเรียกใช้กับ df หลัก เช่น

    df[df["วิธีการชำระเงิน"]=="เงินสด"]

    ผมก็จะสามารถ Filter ข้อมูลได้ (เห็นมะว่ามียอดขายลด10% อยู่)

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 60

    ถ้าจะใส่หลายเงื่อนไข ให้ใช้ & หรือ | มาเชื่อม ซึ่งต้องใส่วงเล็บคั่นแต่ละก้อนด้วย เช่น

    df[(df["วิธีการชำระเงิน"]=="เงินสด") & (df["จำนวนชิ้น"]>3)]
    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 61

    ซึ่งเราสามารถเก็บค่าที่ Filter ได้นี้ไว้ในตัวแปรใหม่ หรือจะเก็บทับตัวแปรเดิมก็ได้ แต่ในที่นี่ผมคิดว่าเก็บไว้ในตัวแปรใหม่ดีกว่า เช่น เก็บไว้ใน cashGT3_DF

    ป.ล. ในตัวแปร df ก็ยังคงมีครบทุกแถวอยู่นะ

    cashGT3_DF=df[(df["วิธีการชำระเงิน"]=="เงินสด") & (df["จำนวนชิ้น"]>3)]

    ผสมผสานกับการใช้ Dropdown List

    แน่นอนว่าเรื่องพวกนี้ เราผสมผสานกับการใช้ Dropdown List เพื่อให้ข้อมูลที่ Filter นั้น Dynamic ได้นะ (ถ้าต้องการ)

    เลือกเงินสด

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 62

    เลือกเครดิต

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 63

    ทำการสรุปเบื้องต้น

    ผมสามารถทำการสรุปด้วยการ Group by แล้วหาผลรวม นับจำนวน ค่ามากสุด น้อยสุด ได้เช่นกัน ดังนี้

    df.groupby(ชื่อคอลัมน์ที่จะ group)

    ซึ่งจะได้ Object พิเศษคือการ Group ตามคอลัมน์ที่เลือก แต่ว่า Object นี้เราไม่สามารถแสดงผลออกมาดูได้ เราจะต้องทำอย่างอื่นต่อด้วย เช่น

    df.groupby(ชื่อคอลัมน์ที่จะ group)[คอลัมน์ที่จะสรุป].วิธีสรุป
    
    #หรือ
    
    df.groupby(ชื่อคอลัมน์ที่จะ group)[ list คอลัมน์ที่จะสรุป ].วิธีสรุป

    เช่น

    df.groupby("สินค้า")[["จำนวนชิ้น","ยอดขาย"]].sum()
    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 64

    วิธีการสรุปอื่นๆ เช่น

    • size() = จำนวนแถว
    • count() = นับที่ไม่ว่าง
    • first() = ตัวแรก
    • last() = ตัวสุดท้าย
    • sum() = หาผลรวม
    • min() = ค่าน้อยสุด
    • max() = ค่ามากสุด
    • median() = ค่ามัธยฐาน

    หรือจะสรุปๆหลายแบบพร้อมกันก็ได้ โดยใช้

    df.groupby(ชื่อคอลัมน์ที่จะ group)[ list คอลัมน์ที่จะสรุป].aggregate(listวิธีสรุป)

    เช่น

    df.groupby("สินค้า")[["จำนวนชิ้น","ยอดขาย"]].aggregate([sum,max])
    
    #หรือ เขียน agg สั้นๆ ก็พอ
    
    df.groupby("สินค้า")[["จำนวนชิ้น","ยอดขาย"]].agg([sum,max])
    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 65

    ลองสร้างกราฟดูบ้าง

    กราฟพื้นฐาน ที่ไม่ต้องใช้ library พิเศษ

    แบบนี้คือใช้ .plot ได้เลย แล้วกำหนดประเภทกราฟที่ต้องการได้อีกที

    df["ยอดขาย"].plot.hist()

    แปลว่า ให้เอาคอลัมน์ยอดขายมาทำ histogram

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 66

    ผลลัพธที่ได้จะเป็นรูปเล็กๆ ใน Cell

    วิธีทำให้เห็นรูปใหญ่ มีหลายแบบ

    1. ขยาย Cell ให้ใหญ่
    2. Merge Cell ให้ใหญ่
    3. ให้เราคลิ๊กขวาแล้วเลือก Picture In Cell -> Create Reference (แนะนำ)
    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 67

    จะมีการสร้างภาพใหญ่ขึ้นมาอีกอัน ที่ link ค่าจาก cell ผลลัพธ์อีกที

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 68

    ประเภทกราฟอื่นๆ เช่น area , bar, barh , box , density , hexbin , hist , kde , line , pie , scatter

    สมมติว่าจะ Plot จากตัวที่ Groupby แล้ว เช่น ยอดขายรวมของสินค้าแต่ละตัวเป็นกราฟแท่ง ก็อาจทำประมาณนี้ได้

    dfProductSales=df.groupby("สินค้า")["ยอดขาย"].sum()
    dfProductSales.plot.bar()

    ผลลัพธ์จะออกมาแบบนี้ คือ ข้อความที่เป็นภาษาไทยนั้นเน่าสนิท…

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 69

    ปกติแล้ว วิธีแก้คือเราต้องประกาศคำสั่งแก้ตระกูล Font ให้เป็นตัวที่อ่านภาษาไทยออกก่อน มันถึงจะแก้ได้

    plt.rcParams['font.family']='Sarabun-Regular.ttf'
    plt.rcParams['font.size']=18
    
    dfProductSales=df.groupby("สินค้า")["ยอดขาย"].sum()
    dfProductSales.plot.bar()

    และถ้าตัวระบบที่รัน Python อยู่ ไม่มี Font นั้นๆ ก็ต้องหาทาง install ก่อน แต่ปัญหาคือ ผมเองก็ยังหาวิธี install font ไทยเข้าไปใน Python ของ Excel ที่มันรันบน cloud ของ Anaconda ที่ร่วมกับ Microsoft ไม่ได้แฮะ ใครเจอวิธีแล้วบอกด้วยนะครับ…

    ตอนนี้ช่างมันเรื่องอ่านภาษาไทยไม่ออกไปก่อนละกันนะ

    ทำกราฟขั้นสูงขึ้น ด้วย Seaborn

    seaborn คือ Library ของ Python ที่สามารถสร้างกราฟสวยๆ customize ได้ง่าย และสามารถ set theme ต่างๆ ได้ง่ายกว่ากราฟแบบทั่วไป

    แต่เราจะต้องมีการ import library เข้ามาก่อน (ทำทีเดียวก็พอ)

    ใช้คำว่า

    import seaborn as sns

    การใช้ as sns คือการตั้งชื่อเล่นให้กับมัน จะได้ไม่ต้องพิมพ์ยาว

    เช่น

    import seaborn as sns
    sns.set_theme(style="dark", palette="pastel")
    boxplot = sns.boxplot(x="วิธีการชำระเงิน", y="ยอดขาย", data=df)
    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 70

    และยังรองรับกราฟแปลกๆ อีกมากมาย เช่น JointPlot

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 71

    ใครอยากลองดู Data ที่ Plot แล้วน่าสนใจขึ้น ลองดู Sample Dataset ที่ทาง Microsoft เตรียมมาก็ได้ครับ กดตามรูปได้เลย

    โดยมีการใช้ Pairplot เพื่อดู Scatter Plot แบบ Cross กันระหว่างหลายคอลัมน์ด้วย

    สอนใช้ Python ใน Excel ตอนที่ 1 : ลองใช้ครั้งแรก 72

    ถ้ายังไม่จุใจ ลองดูตัวอย่างกราฟอื่นๆ ได้ที่นี่ https://seaborn.pydata.org/examples/index.html

    สรุปความเห็นของผม

    เท่าที่ทดลองใช้งาน ผมคิดว่า Python สำหรับ Excel นั้นเกิดมาเพื่อช่วยในการวิเคราะห์ข้อมูล เช่น Data Analysis ด้วย Pandas , หรือทำ Machine Learning ด้วย Scikit Learn และทำ Visualization เจ๋งๆ (ถ้าไม่ติดปัญหาภาษาไทย) มากกว่าที่จะทำงานด้านอื่นๆ ที่สามารถทำได้ด้วย Python แบบปกติที่เรา install ลงในเครื่องตัวเอง

    ข้อเสีย

    • ข้อจำกัดสำคัญคือการที่ Python ตัวนี้มันดัน Run บน Cloud ทำให้เราต้องต่อ Net และรันได้ค่อนข้างช้า และรับข้อมูลมากๆ ไม่ไหว
    • จุกจิกเรื่อง Security ที่ให้ไม่สามารถใช้ Library บางอย่าง เช่น Scrape Data จากเว็บไซต์ก็ไม่ได้
    • แถมในอนาคตไม่รู้จะมีการเก็บตังเพิ่มเพื่อให้ใช้ Feature นี้ได้เต็มที่อีกรึเปล่าด้วยนะ…

    ข้อดี

    อย่างไรก็ตาม การใช้ Python ใน Excel มีข้อดีคือ

    • สามารถเห็นภาพของข้อมูลได้ง่าย
    • ผสมผสานกับเครื่องมือต่างๆ ของ Excel ได้ดี
    • เหมาะกับคนที่ใช้ Excel ได้แล้วระดับนึง แล้วอยากลองหัดใช้ Python แต่จะให้ไปเขียนโปรแกรมใน Editor ล้วนๆ หรือเขียนใน Colab เลยอาจจะรู้สึกไม่ชิน ดังนั้นมาหัดใน Excel ที่ถนัดก่อนอาจเป็นวิธีเริ่มที่ดีก็ได้นะครับ
    • Python เองก็มีความสามารถหลายอย่างที่ทำได้ยากใน Excel
      • การ Manipulate ข้อความก็เป็นจุดแข็งของ Python เมื่อเทียบกับ Excel
        • Python นั้นใช้ Regular Expression ได้ แต่ Excel ปกติทำไม่ได้
        • Python สามารถใช้ Machine Learning ทำความเข้าใจประโยคได้
      • ใน Python เรายังสั่งวน Loop ทำเรื่องต่างๆ ได้ง่ายกว่า Excel มาก (ใน Excel วน Loop ด้วย LAMBDA Helper ยากกว่าเยอะ)
      • การทำ Data Analysis ขั้นสูง เช่น การ Predict ด้วย Machine Learning นั้น Excel สู้ไม่ได้เลย

    ตอนต่อไป

    ดังนั้นในตอนถัดไป ผมจะพูดถึงเรื่องการวน Loop การจัดการ List และการเขียน Condition ใน Python ครับ ไปอ่านต่อได้เลย

  • สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter

    ในบทความนี่ผมจะมาแนะนำเครื่องมือ ChatGPT Code Interpreter และเขียนแนวทางการ Prompt และ แนวทางการวิเคราะห์ข้อมูลด้วย AI เพิ่มเติมจากที่ทำไปในคลิป เพราะยังมีอีกหลายประเด็นที่น่าสนใจ แต่เวลารอบแรกหมดซะก่อน ผมเลยจะจะทยอยทำการถามเพิ่มเติมแล้วเอามาสรุปเทคนิคทั้งหมดในบทความนี้ให้ด้วยครับ

    อะไรคือ ChatGPT Code Interpreter?

    มันคือความสามารถของ AI ที่ชื่อว่า ChatGPT ที่สามารถให้เรา Upload ไฟล์เข้าไปแล้วสั่งทำงานเกี่ยวกับไฟล์นั้นได้

    ซึ่งมันจะใช้ Python ในการแก้ปัญหาให้เรา ซึ่งเป็นภาษาที่ใช้ไม่ยาก และมีความสามารถหลากหลายมากๆ โดยเฉพาะมี Library มหาศาลในการแก้ปัญหาต่างๆ และนี่คือตัวอย่าง Library ที่ Code Interpreter ใช้ได้ (อาจมีมากกว่านี้อีก)

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 73
    https://twitter.com/Gavriel_Cohen/status/1678131137703473153/photo/1

    ซึ่งแปลว่าเจ้า Code Interpreter มันทำอะไรได้หลายอย่างมากๆๆ เช่น วิเคราะห์ข้อมูล ช่วยแก้ code แปลงไฟล์ หรือจะอ่านข้อมูลรูปภาพ หรือช่วยทำ Slide รวมเป็น PDF ก็ยังได้

    ผมอยากให้มองว่า ChatGPT Code Interpreter เป็นเหมือนลูกน้องของเรา ที่เก่งเรื่อง การเขียนโปรแกรม Python โดยเฉพาะมีความสามารถเรื่อง Data Science เป็นอย่างดี และก็มีความฉลาดในเรื่องทั่วๆ ไปได้ดีพอๆ กับมนุษย์คนนึงเลย

    แต่ถึงจะเก่งแค่ไหนก็ตาม ยังไงก็มีความเป็นไปได้ที่ลูกน้องคนนี้จะทำงานผิด… ดังนั้นเราในฐานะของหัวหน้า ก็มีหน้าที่จะต้องตรวจงานและสั่งลูกน้องแก้ไขงาน ให้เป็นไปตามที่เราต้องการก่อนจะนำผลไปใช้จริงได้

    ที่เจ๋งคือ ลูกน้องคนนี้เต็มใจที่จะช่วยเราเต็มที่แบบไม่บ่นเลย เราสั่งให้แก้อะไรก็แก้เต็มที่ (จนกว่าจะเต็ม Limit 25 Message ทุกๆ 3 ชั่วโมงของ GPT-4 อ่ะนะ 5555)

    ใครที่ยังไม่ได้ดูคลิป ลองไปดูก่อนได้ครับ

    คลิปการทดสอบครั้งแรก

    การเรียกใช้ Code Interpreter

    • ต้องใช้ ChatGPT Plus ถึงจะใช้ฟีเจอร์ใหม่ที่ชื่อว่า Code Interpreter ได้ (ต้องจ่ายเงินเดือนละ 20$ USD ซึ่งคุ้มค่ามากๆ ขอบอก เพราะนอกจากจะใช้ฟีเจอร์นี้ได้ ก็ยังใช้ Plugin อื่นได้อีกมหาศาล และนอกจากนั้นตัว GPT4 ของ ChatGPT Plus ก็ฉลาดกว่าตัว GPT3.5 ของแบบฟรีมากๆ ด้วย)
    • ต้องไปเปิด Settings ในส่วนของ Beta features ก่อนด้วย ถึงจะใช้ได้
    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 74

    แล้วต้องเลือก Dropdown ที่เป็นโหมด Code Interpreter ด้วย

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 75

    การ Upload ไฟล์

    ซึ่งจะได้ความสามารถของการ upload ไฟล์ และการจัดการไฟล์นั้นด้วย Python เพิ่มมานั่นเอง

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 76

    โดยที่เรา Upload ไฟล์ได้ทีละไฟล์ แต่ก็มีเทคนิคหลายอย่างที่ทำให้วิเคราะห์ข้อมูลหลายอันได้ เช่น

    • สามารถ Upload ไฟล์ได้หลายรูปแบบนะ จะ Upload รูปให้มันอ่านข้อมูล “จากในรูป” ก็ยังได้เลย ตัวอย่างไฟล์ที่ใช้ได้ เช่น ไฟล์ยอดนิยมอย่าง TXT, CSV, JSON, XML, XLSX, PDF, DOCX, PPTX, ODP, JPEG, PNG, WAV, MP3, MP4, AVI, MOV, PY, HTML, PDF เป็นต้น
    • สามารถ Upload ได้หลายรอบ เพื่อเอาข้อมูลแต่ละอันมาวิเคราะห์ร่วมกันได้ด้วย
    • สามารถ Zip ไฟล์ก่อนค่อย Upload ได้ (ในคลิปผมสั่งให้มันรวมหลายไฟล์เข้าด้วยกันแล้วค่อยวิเคราะห์)

    ปกติเวลา Upload ไฟล์ Data แล้ว มันจะทำการอ่านว่ามี Data ประมาณไหน มีคอลัมน์อะไรบ้าง? ถ้าคอลัมน์เป็นภาษาไทย มันก็แปลเป็น Eng ได้ด้วยอัตโนมัติเลย ฉลาดมากๆ

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 77

    จากนั้นก็ถึงตาเราที่จะสั่งงานมันต่อแล้ว ซึ่งวิธีสั่งก็คือพิมพ์ภาษามนุษย์เข้าไปได้เลย แต่ผมแนะนำให้สั่งด้วยภาษาอังกฤษ AI มันจะเก่งกว่า และทำงานเร็วกว่าภาษาไทยมาก

    สรุปเทคนิคการ Prompt ด้านวิเคราะห์ข้อมูล

    ดูภาพรวมของข้อมูลก่อน

    ถ้าเราไม่รู้จะวิเคราะห์อะไร ก็สั่งให้มันทำกราฟหลายๆ แบบเพื่อสำรวจข้อมูลก่อนก็ได้

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 78
    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 79

    ถ้าสั่งทำกราฟแล้วมีปัญหากับข้อความภาษาไทย (กลายเป็นกล่องเหลี่ยมๆ) ก็สั่งแก้ข้อมูลให้เป็นภาษาอังกฤษก่อนแล้วค่อยทำกราฟก็ได้

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 80

    ลองสร้างกราฟหลายๆ แบบ เพื่อให้เห็นมุมมองที่หลากหลาย

    ถ้าเรามีความต้องการอยากรู้อะไรแบบเจาะจงก็สั่งมันได้เลย

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 81

    แต่ถ้ามันทำมาไม่ตรงใจ ก็สั่งแก้ แค่นั้นเอง

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 82

    อยากให้แกน y ของกราฟเริ่มที่ 0 ก็สั่งได้

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 83

    สั่งทำแกน y 2 ฝั่งก็ได้

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 84

    พยายามตรวจเช็คความถูกต้องของสิ่งที่มันทำด้วย

    ถ้าเราสงสัยหรือไม่แน่ใจว่า มันคำนวณสิ่งต่างๆ ด้วยวิธีไหนกันแน่ ถูกต้องหรือไม่ ก็แค่ถามมันดู

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 85

    บางครั้งสิ่งที่มันวิเคราะห์ออกมาแล้วดูแปลกๆ เราเองก็ต้อง “เอ๊ะ” ให้เป็น แล้วสั่งแก้ไข เช่นในรูปข้างล่าง ข้อมูลของช่วงเวลา 0 นาฬิกามันเยอะผิดปกติ ทั้งนี้เป็นเพราะมีข้อมูลที่เป็นค่าว่าง ถูกตีความปนเป็น 0 ไปด้วย

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 86

    ถ้าสั่งแก้ไข ก็จะได้ผลลัพธ์ที่ถูกต้อง

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 87

    สามารถสั่งให้มันเอาผลลัพธ์ออกมาได้

    สั่งให้มัน export ข้อมูลที่รวมไฟล์และเพิ่มคอลัมน์ต่างๆ มาแล้ว ออกมาเป็น csv ก็ได้

    สอนใช้ AI ช่วยวิเคราะห์ข้อมูล ด้วย ChatGPT Code Interpreter 88

    สรุป

    AI คือลูกน้องที่มีความสามารถสูง ดังนั้น ถ้าเราจะเป็นหัวหน้าของ AI ได้ เราก็ควรจะต้องมีความสามารถสูงพอที่พอจะเข้าใจหรือตรวจสอบสิ่งที่ AI ทำมาให้ด้วยเช่นกัน เพราะสุดท้าย เราเองคือคนที่ต้องรับผิดชอบในผลงานนั้นๆ อยู่ดี

    ดังนั้น แม้ว่าในอนาคต AI จะมาช่วยงานเรามากขนาดไหน เราก็ต้องมีทักษะและความรู้ที่เพียงพอด้วยเช่นกัน ยังทิ้งไม่ได้นะครับ และสุดท้าย คนที่มีความรู้ความเข้าใจมากพอ และยังมี AI มาช่วยงานด้วยอีก จะทำผลงานได้ยอดเยี่ยมและรวดเร็ว มากกว่าคนที่ไม่มีความรู้แต่พึ่งพาแค่ AI หรือดีกว่าคนที่มีความรู้แต่ไม่ใช้ AI ด้วยเช่นกัน

  • แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX

    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX

    ปัญหาหนึ่งที่คนที่ใช้ Power BI มาแล้วซักพักจะต้องเจอก็คือการเรียนรู้เรื่องสูตร DAX ซึ่งเป็นภาษาที่มีความสามารถในการสร้างผลลัพธ์ที่ซับซ้อนได้เก่งมากๆ แต่ก็ค่อนข้างยากต่อการเรียนรู้และต่อยอด เนื่องด้วยประเด็นต่างๆ เหล่านี้

    เหตุผลที่ทำให้ DAX ยาก

    เหตุผล 1 : การเขียน DAX ขึ้นอยู่กับบริบท และ Data Model

    DAX นั้นไม่เหมือนภาษาอื่นๆ ตรงที่การทำงานของมันขึ้นกับสิ่งแวดล้อมหรือบริบทที่เรียกว่า Evaluation Context อย่างเข้มข้น (ทั้ง Filter Context และ Row Context) ดังนั้นมันจึงมีความเกี่ยวข้องกับ Data Model อย่างเข้มข้นด้วย

    สมมติว่าเราไปถามสูตร DAX คนอื่น ก็จะเป็นการยากที่ผู้รู้หรือแม้แต่ ChatGPT จะตอบให้เราได้ถูกต้องแบบง่ายๆ เพราะว่าเราต้องอธิบายตัวข้อมูลและ Data Model ของเราพอสมควร ผู้รู้ถึงจะตอบคำถามของเราได้

    ไม่เหมือนสูตร Excel ที่รู้แค่ที่อยู่ของข้อมูลว่าเก็บอยู่ใน Range ไหนหรือ Table ไหนคอลัมน์ไหนก็เพียงพอแล้ว

    Solution ที่แนะนำ

    ณ ตอนนี้ ถ้าจะถามคำถามเรื่อง Power BI กับคนอื่น ควรจะให้ข้อมูลที่สำคัญให้ครบ เช่น

    • อยากทำอะไรได้ ทำไปทำไม
    • หน้าตาค่าของผลลัพธ์ที่อยากได้ วิธีคำนวณสิ่งที่อยากได้ ถ้าทำ Manual เอง
    • หน้าตาของข้อมูลที่เกี่ยวข้อง
    • หน้าตาของ Data Model
    • จะเอาสูตรไปใช้ในไหน ใน Measure หรือ Column หรือ Table
    • ถ้าเป็น Measure จะเอาไปใช้ใน Visual อะไร มี Filter อะไรทำงานอยู่ หรือจะเกิดการ Filter อะไรขึ้นได้บ้าง

    เห็นมั้ยครับว่ามันยาก เพราะว่า “การที่จะได้คำตอบที่สมบูรณ์ คำถามก็ต้องสมบูรณ์ด้วย”

    แต่ในอนาคตอันใกล้นี้ เราจะมีแนวทางใหม่ที่ง่ายขึ้นเยอะ นั่นคือ การสอบถามสูตร DAX ที่น่าจะสะดวกสุด คือ “ถาม AI ที่อยู่ในโปรแกรมเลย” เช่น

    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 89
    อันนี้คือ Quick Measure Suggestion ซึ่งใช้ช่วยทำ Measure ที่ง่ายๆ ได้

    ซึ่งสะดวกที่มันมองเห็น Data เราอยู่แล้วจึงไม่ต้องอธิบายมาก แต่เราอย่าเพิ่งเชื่อมัน 100% ทันที เพราะเราต้องเอาสิ่งที่มันตอบมาทดสอบด้วย และ เราเองก็ต้องเข้าใจสูตรที่ AI จะ Generate ออกมาให้ด้วยเช่นกัน เพราะ สุดท้ายเราเองก็ยังต้องเป็นคนรับผิดชอบในงานนั้นอยู่ดี

    นั่นคือ แม้ในอนาคต AI จะเขียนสูตรหรือสร้างรายงานให้เราได้ เราก็ต้องทำเองเป็นอยู่ดี (แค่อาจไม่ต้องทำเองแต่แรก)

    เหตุผล 2 : การเขียน DAX นั้นตรวจสอบการทำงานยาก

    DAX นั้นเป็นสูตรที่สามารถเขียนแบบซับซ้อนมากๆ ได้ แต่ยิ่งเขียนซับซ้อนมากเท่าไหร่ ก็จะยิ่งตรวจสอบผลลัพธ์ได้ยากขึ้นด้วยเช่นกัน

    แม้ว่าเราจะสามารถประกาศตัวแปรด้วยการใช้ VAR และ RETURN มาช่วยแล้ว แต่ผลลัพธ์ในบางขั้นตอนนั้นก็ไม่สามารถเอาออกมาได้ตรงๆ เช่น

    สมมติว่าเราเขียน DAX Measure แต่ถ้าผลลัพธ์ใน VAR ขั้นตอนกลางๆ เป็นตาราง ก็เอาออกมาดูตรงๆ ใน Measure ไม่ได้อีก

    Solution ที่แนะนำ

    วิธีที่ Work ที่สุดในการตรวจสอบการทำงานของสูตร DAX ก็คือ การใช้เครื่องมือเสริมที่เรียกว่า DAX Studio นั่นเอง

    นอกจากที่มันช่วย Export ข้อมูลเยอะๆ (เกินล้าน) ออกจาก Data Model ได้แล้ว มันยังช่วยเขียนหรือตรวจสอบสูตร DAX ได้ด้วย

    ซึ่งนี่คือเหตุผลหลักที่ผมเขียนบทความนี้ขึ้นมา นั่นก็คือเพื่อ อธิบายแนวทางการใช้ DAX Studio ในการช่วยเขียนและตรวจสอบสูตร DAX นั่นเอง

    วิธีการเรียกใช้ DAX Studio

    หลังจากที่ Install DAX Studio เรียบร้อยแล้ว ให้เราเปิดไฟล์ PBIX ของเราตามปกติ แล้วไปที่ External Tool แล้วจะมี Icon DAX Studio ใน Ribbon ก็กดเข้าไปได้เลย มันจะเชื่อมกับ Data Model ของไฟล์เราโดยอัตโนมัติ และมองเห็นข้อมูลและ Measure ทั้งหมด

    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 90

    สิ่งที่เราจะต้องทำใน DAX Studio คือ สร้าง DAX Query ขึ้นมา เพื่อให้ได้คำตอบหรือผลลัพธ์ที่เราอยากรู้ ซึ่งมีคู่มือการใช้งานการสร้าง Query หลักๆ ที่นี่ แต่ถ้าขรเดี๋ยวผมจะแนะนำให้เอง

    Syntax หลักของ Query ใน DAX Studio

    เวลาใช้ DAX Studio โครงสร้างของ Query หลักๆ คือ แบบนี้

    EVALUATE <table expression>

    ซึ่ง table expression แปลว่าสูตรอะไรก็ได้ที่ “ให้ผลลัพธ์เป็นตาราง” นั่นคือใส่ชื่อตารางเข้าไปตรงๆ เลยก็ยังได้ เช่น

    EVALUATE ProductMaster

    เราจะได้ผลลัพธ์เรียงตาม Data ใน Data Model

    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 91

    หรือเราจะใส่สูตรอะไรก็ได้ ที่ให้ผลเป็นตาราง ซึ่งก็คือพวก Table Function ทั้งหลายนั่นเอง เช่น DISTINCT, ALL, FILTER, CALCULATETABLE, SUMMARIZE, CROSSJOIN, ADDCOLUMNS และอีกมากมาย

    เช่น

    EVALUATE ALL(ProductMaster[Brand],ProductMaster[Color])
    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 92

    ใช้ Query Builder ก็ได้

    แต่ถ้าเรายังเขียนสูตรไม่เก่ง หรือ ขี้เกียจเขียน เราก็ไม่จำเป็นต้องเขียน Query เอง เพราะมันมี Query Builder ให้ใช้ โดยที่มันจะ Gen Query ให้เราได้

    เพียงแค่ ราสามารถลากเข้าไปได้ว่าตาราง Query เรา มีผลลัพธ์เป็นคอลัมน์อะไรบ้าง มีการ Filter ด้วยอะไร และเรียงตามอะไร??

    /* START QUERY BUILDER */
    EVALUATE
    SUMMARIZECOLUMNS(
        ProductMaster[Category],
        ProductMaster[Class],
        "Total Revenue", [Total Revenue]
    )
    ORDER BY 
        ProductMaster[Category] ASC,
        ProductMaster[Class] ASC
    /* END QUERY BUILDER */
    DAX Studio Query Builder

    วิธีแสดงค่า Measure ที่เป็น scalar ออกมา

    ถ้าเราลองใส่ Measure เข้าไปใน Query Builder เฉยๆ มันจะ Gen ให้ออกมาแบบนี้ ซึ่งเป็นหนึ่งในวิธีได้ผลลัพธ์ออกมาเป็นตารางได้ แม้จะแสดงค่าใน Measure ก็ตาม

    /* START QUERY BUILDER */
    EVALUATE
    CALCULATETABLE(
        ROW(
        "Total Revenue", [Total Revenue],
        "Total Qty", [Total Qty]
        )
    )
    /* END QUERY BUILDER */
    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 93

    แต่ถ้าเราต้องการแสดง Measure ตัวเดียว วิธีที่ Simple ที่สุดคือใช้ { } ซึ่งเป็น Table Constructor มาช่วย เช่น

    EVALUATE {[MeasureName]}

    เช่น

    EVALUATE {[Total Revenue]}
    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 94

    ดู History ย้อนหลัง

    นอกจากนี้เรายังกดดู History ย้อนหลังของ Query ที่เราทำทั้งหมดได้ ไม่ว่าจะพิมพ์เองหรือใช้ Query Builder ก็ตาม โดย Double Click ที่ Query ที่ต้องการเพื่อเอากลับมาใช้ใหม่ได้

    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 95

    แล้วมันใช้ตรวจสอบสูตร DAX ยังไง?

    สมมติผมเขียนสูตร DAX ขึ้นมาแล้วผมไม่แน่ใจว่ามันทำงานถูกต้องหรือไม่ ผมสามารถใช้ DAX Studio มาช่วยตรวจสอบได้

    ยกตัวอย่างเช่น

    ผมต้องการจะคำนวณว่า มีกี่ Brand ที่สามารถขายของได้เกิน 10000 ชิ้นทุกปี แล้ว brand เหล่านั้นสร้างยอดขายได้ทั้งหมดกี่บาท??

    โดยแนวคิดคือ ผมจะ

    • List รายชื่อ Brand ที่ขายได้เกิน 10000 ชิ้นในแต่ละปีออกมาก่อน เรียกว่า BigBrand
    • List รายชื่อ Brand ที่ขายได้ไม่เกิน 10000 ชิ้นในแต่ละปี เรียกว่า NotBigBrand
    • เอารายชื่อ BigBrand ตั้งแล้วหักที่มีใน NotBigBrand ออก เราก็จะได้เหลือ Brand ที่ผ่านเกณฑ์เท่านั้น

    สมมติว่าแรกสุดผมเขียนสูตร Measure ไปแบบนี้ (ซึ่งผิด)

    Total Brand buy Gt10000 every year = 
    VAR BrandandYear=SUMMARIZE(OrderDetail,dDate[Year],ProductMaster[Brand])
        
    VAR BigBrand=CALCULATETABLE(DISTINCT(ProductMaster[Brand]),
    FILTER(BrandandYear,[Total Qty]>10000))
    
    VAR NotBigBrand=CALCULATETABLE(DISTINCT(ProductMaster[Brand]),
    FILTER(BrandandYear,[Total Qty]<=10000))
    
    VAR BigBrandEveryYear=EXCEPT(BigBrand,NotBigBrand)
    VAR CalRev=CALCULATE([Total Revenue],BigBrandEveryYear)
    RETURN COUNTROWS(BigBrandEveryYear)
    //RETURN CalRev

    ที่ผิดเพราะมันกลับให้ผลลัพธ์รายชื่อ Brand ในแต่ละปีไม่เท่ากัน (ตามหลักต้องนับได้จำนวนเท่ากัน)

    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 96

    ที่นี้ผมต้องการเอาไปทดสอบใน DAX Studio ผมสามารถทำได้ดังนี้ โดยผมอยากตรวจสอบที่ปี 2022 ด้วยว่าทำไมมันถึงได้ 10

    นอกจากนี้ ก็ลองเอา Measure ที่เราทำมาใช้ใน DAX Studio ดูซึ่งมันใช้ KEEPFILTERS มาช่วยจัดการเรื่อง Filter ให้เลย สะดวกดี

    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 97

    ที่นี้หากผมอยากจะลองแก้ไขค่าใน Measure ให้สามารถเห็นตารางผลลัพธ์ในขั้นตอนระหว่างกลางได้ ผมก็สามารถกด edit Measure ได้

    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 98

    ที่นี้มันด่าเรา เนื่องจากว่าเราดันให้ผลลัพธ์ของ Measure เป็นตาราง มันเลยทำไม่ได้

    ผมเลยต้องพลิกแพลงเล็กน้อย คือ ให้ Copy สูตรใน Measure ออกมา แล้วเอามาทับในส่วนสูตร ROW ที่มัน Gen ให้ (เพราะสิ่งที่เรา copy มาคือตาราง)

    /* START QUERY BUILDER */
    EVALUATE
    CALCULATETABLE(
    
    VAR BrandandYear=SUMMARIZE(OrderDetail,dDate[Year],ProductMaster[Brand])
        
    VAR BigBrand=CALCULATETABLE(DISTINCT(ProductMaster[Brand]),
    FILTER(BrandandYear,[Total Qty]>10000))
    
    VAR NotBigBrand=CALCULATETABLE(DISTINCT(ProductMaster[Brand]),
    FILTER(BrandandYear,[Total Qty]<=10000))
    
    VAR BigBrandEveryYear=EXCEPT(BigBrand,NotBigBrand)
    VAR CalRev=CALCULATE([Total Revenue],BigBrandEveryYear)
    //RETURN COUNTROWS(BigBrandEveryYear)
    //RETURN CalRev
    RETURN BigBrandEveryYear
        
        ,
        KEEPFILTERS( TREATAS( {2022}, dDate[Year] ))
    )
    /* END QUERY BUILDER */
    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 99

    คราวนี้มันจะ Run ออกมาดูได้แล้ว และยังมีผลการ Filter ปี 2022 อยู่ด้วย

    ทีนี้ผมก็สามารถลองเอา BigBrand และ NotBigBrand ออกมาได้เช่นกัน ซึ่งใน NotBigBrand พบว่ามี Brand เดียวที่ไม่ผ่านเกณฑ์ คือ Northwind Trader

    ซึ่งเราลองสร้าง Query อีกอันมาเช็คดูจริงๆ ว่าในแต่ละปี แต่ละ brand ขายได้เท่าไหร่

    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 100

    เราจะพบว่ามีหลาย Brand ที่ไม่ควรผ่าน แต่มันดันเจอแค่ Northwind Trader อันเดียว น่าจะเป็นเพราะมันเป็น Brand เดียวที่ไม่ผ่านในปี 2022

    ทีนี้ผมเลยลองเอาส่วนของ BrandandYear ออกมาดูดังนี้

    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 101
    /* START QUERY BUILDER */
    EVALUATE
    CALCULATETABLE(
    
    VAR BrandandYear=SUMMARIZE(OrderDetail,dDate[Year],ProductMaster[Brand])
        
    VAR BigBrand=CALCULATETABLE(DISTINCT(ProductMaster[Brand]),
    FILTER(BrandandYear,[Total Qty]>10000))
    
    VAR NotBigBrand=CALCULATETABLE(DISTINCT(ProductMaster[Brand]),
    FILTER(BrandandYear,[Total Qty]<=10000))
    
    VAR BigBrandEveryYear=EXCEPT(BigBrand,NotBigBrand)
    VAR CalRev=CALCULATE([Total Revenue],BigBrandEveryYear)
    //RETURN COUNTROWS(BigBrandEveryYear)
    //RETURN CalRev
    RETURN BrandandYear
        
        ,
        KEEPFILTERS( TREATAS( {2022}, dDate[Year] ))
    )
    /* END QUERY BUILDER */

    พบว่า Filter ปี 2022 ส่งผลเข้าไปใน BrandandYear ทำให้เห็นปีไม่ครบนั่นเอง ทั้งนี้เพราะเราใช้ SUMMARIZE ซึ่งจะทำงานภายใต้ Filter Context แบบไม่ได้มีการปลด Filter ออก

    ดังนั้นเราจะเปลี่ยนมาเป็นการใช้ CROSSJOIN + ALL เพื่อให้เห็นทุกปีเสมอ ดังนี้

    โดยเปลี่ยนจาก

    VAR BrandandYear=SUMMARIZE(OrderDetail,dDate[Year],ProductMaster[Brand])

    เป็น

    VAR BrandandYear=CROSSJOIN(ALL(dDate[Year]),ALL(ProductMaster[Brand]))

    /* START QUERY BUILDER */
    EVALUATE
    CALCULATETABLE(
    
    VAR BrandandYear=CROSSJOIN(ALL(dDate[Year]),ALL(ProductMaster[Brand]))
        
    VAR BigBrand=CALCULATETABLE(DISTINCT(ProductMaster[Brand]),
    FILTER(BrandandYear,[Total Qty]>10000))
    
    VAR NotBigBrand=CALCULATETABLE(DISTINCT(ProductMaster[Brand]),
    FILTER(BrandandYear,[Total Qty]<=10000))
    
    VAR BigBrandEveryYear=EXCEPT(BigBrand,NotBigBrand)
    VAR CalRev=CALCULATE([Total Revenue],BigBrandEveryYear)
    //RETURN COUNTROWS(BigBrandEveryYear)
    //RETURN CalRev
    RETURN BrandandYear
        
        ,
        KEEPFILTERS( TREATAS( {2022}, dDate[Year] ))
    )
    /* END QUERY BUILDER */

    สรุปได้แบบนี้ ซึ่งจะเห็นว่าแม้จะมี Filter ปี 2022 ไว้จาก KEEPFILTERS ก็ตาม แต่ว่าเราก็สามารถสร้างตารางที่เห็นทุกปีได้แล้ว

    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 102

    ซึ่งพอเอาไปใช้ใน Filter แล้วเราใส่ Measure เข้าไปว่า [Total Qty] มันก็จะเกิด Context Transition ขึึ้นทำให้ได้ยอดของ Brand นั้นๆ ในปีนั้นๆ จนครบได้ ผล BigBrandEveryYear ที่ถูกต้องจึงออกมาแบบนี้

    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 103

    และนี่คือ 6 Brand ที่ทำยอด Total Qty ได้มากกว่า 10000 ชิ้นทุกปี

    และถ้าเราเอา BigBrandEveryYear นี้ไปเป็น Filter ของ CALCULATE ก็จะหาได้ว่า Brand เหล่านี้สร้าง Total Revenue ได้เท่าไหร่

    Total Revenue by Brand buy Gt10000 every year = 
    VAR BrandandYear=CROSSJOIN(ALL(dDate[Year]),ALL(ProductMaster[Brand]))
        
    VAR BigBrand=CALCULATETABLE(DISTINCT(ProductMaster[Brand]),
    FILTER(BrandandYear,[Total Qty]>10000))
    
    VAR NotBigBrand=CALCULATETABLE(DISTINCT(ProductMaster[Brand]),
    FILTER(BrandandYear,[Total Qty]<=10000))
    
    VAR BigBrandEveryYear=EXCEPT(BigBrand,NotBigBrand)
    VAR CalRev=CALCULATE([Total Revenue],BigBrandEveryYear)
    //RETURN COUNTROWS(BigBrandEveryYear)
    RETURN CalRev

    รวมถึงถ้าเอาไปใช้ใน CONCATENATEX ก็เอารายชื่อ Brand ออกมาเป็นข้อความเดียวได้

    Brand List buy Gt10000 every year = 
    VAR BrandandYear=CROSSJOIN(ALL(dDate[Year]),ALL(ProductMaster[Brand]))
        
    VAR BigBrand=CALCULATETABLE(DISTINCT(ProductMaster[Brand]),
    FILTER(BrandandYear,[Total Qty]>10000))
    
    VAR NotBigBrand=CALCULATETABLE(DISTINCT(ProductMaster[Brand]),
    FILTER(BrandandYear,[Total Qty]<=10000))
    
    VAR BigBrandEveryYear=EXCEPT(BigBrand,NotBigBrand)
    VAR BrandList=CONCATENATEX(BigBrandEveryYear,ProductMaster[Brand],UNICHAR(10),[Total Revenue],DESC)
    //RETURN COUNTROWS(BigBrandEveryYear)
    RETURN BrandList

    เอาไปใช้ในรายงาน ได้แบบนี้

    แนวทางการใช้ DAX Studio ในการตรวจสอบและเรียนรู้ DAX 104

    สรุป

    นี่คือสิ่งที่ DAX Studio ช่วยเราได้ในกรณีที่ต้องเขียนสูตร DAX ที่เริ่มมีความซับซ้อนครับ เรายังไม่รู้ว่าในอนาคต AI จะเก่งพอที่จะเขียน DAX ได้อย่างถูกต้องเลยหรือไม่ ดังนั้นถ้าให้มันช่วยเขียนเราก็ต้องเข้าใจสิ่งที่มันเขียนด้วยอยู่ดี ซึ่ง DAX Studio ก็จะเป็นตัวที่ช่วยได้ในระดับนึงเลย

    สุดท้ายนี้หวังว่าเพื่อนๆ จะพอเข้าใจการทำงานของ DAX Studio มากขึ้น และสามารถนำมาช่วยปรับปรุงการเขียนสูตร DAX ของเราได้นะครับ

  • เจาะลึก ControlNet ใน Stable Diffusion [Part8]

    เจาะลึก ControlNet ใน Stable Diffusion [Part8]

    จากประสบการณ์ที่ใช้เครื่องมือ AI Gen รูปมาหลายตัว พบว่า สิ่งที่ทำให้ Stable Diffusion โดดเด่นมากเมื่อเทียบกับเครื่องมืออื่นๆ นั่นก็คือสิ่งที่เรียกว่า ControlNet นั่นเอง

    แม้ว่าก่อนหน้านี้ผมจะมีการเขียนถึง ControlNet ไปแล้วทีนึง แต่มันก็ผ่านมาหลายเดือนแล้ว (หลายเดือนในโลกของ AI นี่คือนานมากกกก) ตอนนี้ ControlNet เก่งขึ้นกว่าเดิมมากๆ แล้ว จนผมต้องขอกลับมาพูดถึงมันอีกครั้ง โดยที่นอกจากจะอธิบายว่าแต่ละตัวคืออะไรแล้ว จะบอกวิธีการประยุกต์ใช้งานด้วย จะได้สามารถเอาไปใช้ได้ในงานจริงๆ ได้ครับ

    ก่อนอื่นให้ทุกคน Update ControlNet เป็น Version ล่าสุดเท่าที่จะทำได้ (ณ ที่ผมเขียนบทความอยู่นี่ ผมใช้ ControlNet v1.1.224 ซึ่งจะมี Model Preprocessor ให้เลือกใช้มากมายเลยล่ะ แต่ในอนาคตก็คงโผล่เพิ่มมาอีกเรื่อยๆ

    รวมบทความ Stable Diffusion

      Setting ต่างๆ ที่สำคัญของ ControlNet ที่ควรรู้จัก

      Mode ของ ControlNet

      • Balanced : เป็นโหมดการทำงานมาตรฐาน ซึ่ง ControlNet จะถูกใช้กับทั้งส่วน Conditioned และ Unconditioned (ในขั้นตอนการสุ่มตัวอย่าง
      • My prompt is more important: เน้นที่ Prompt ของเรามากกว่า โดยทำให้ผลของ ControlNet จะลดลงอย่างเรื่อย ๆ ตลอดการทำงานของ U-Net
      • ControlNet is more important : เน้นความสำคัญของ ControlNet โดย CFG scale จะทำหน้าที่เป็นตัวคูณสำหรับผลของ ControlNet ด้วย

      Resize mode

      ควบคุมวิธีการจัดการเมื่อขนาดของภาพต้นฉบับมี “อัตราส่วนของรูป” ที่แตกต่างจากขนาดของภาพที่เราจะ Gen ออกมา

      • Just Resize : ปรับขนาดความกว้างและความสูงภาพต้นฉบับ (เช่น ภาพมนุษย์ก้าง) เพื่อให้พอดีกับอัตราส่วนภาพที่เราจะ Gen
      • Crop and Resize : Crop ภาพต้นฉบับ (เช่น ภาพมนุษย์ก้าง) เพื่อให้พอดีกับอัตราส่วนภาพที่เราจะ Gen
      • Resize and fill : ปรับขนาดภาพต้นฉบับ (เช่น ภาพมนุษย์ก้าง) ให้พอดีกับอัตราส่วนโดย”เติมค่าที่ว่างเปล่า” เพื่อให้มีขนาดเท่ากับภาพที่เราจะ Gen

      ป.ล. ถ้าติ๊ก Pixel Perfect มีการปรับขนาดให้เหมาะสมโดยอัตโนมัติ ผมนะนำให้ติ๊กด้วย

      ความสามารถของแต่ละ Model และ Preprocessor ที่ใช้คู่กัน

      ผมจะพยายามเรียง Model ที่คล้ายกันมาอยู่ด้วยกัน (ไม่ได้เรียงตามตัวอักษร) เพื่อนๆ จะได้เทียบความแตกต่างได้ชัดเจนขึ้นนะครับ

      หมายเหตุ : ทุก Model อาจจะใช้ Preprocessor เป็น None ได้ หากว่ารูปต้นฉบับผ่านการ Process มาตามที่ Model นั้นๆ ต้องการเรียร้อยแล้ว เช่น Model Openpose ต้องการรูปมนุษย์ก้าง หากรูปที่เราใส่เป็นมนุษย์ก้างอยู่แล้ว ก็ใช้แบบ None ได้เลย

      หมายเหตุ 2 : เราสามารถเปิดใช้ ControlNet ได้หลายตัวพร้อมกัน (Multi-ControlNet) โดยไปเปิดใน Setting ได้นะครับ

      Prompt ที่ใช้

      (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (beautiful woman wearing jeans:1.4)
      
      Negative prompt: paintings, sketches, (worst quality,low quality,normal quality:2), lowres, ((monochrome)), ((grayscale))
      
      Steps: 30, Sampler: DPM++ SDE Karras, CFG scale: 7, Seed: 1352808870, Size: 512x768, Model hash: e4a30e4607, Model: majicmixRealistic_v6, Version: v1.3.0
      

      ผลที่ได้ (แบบไม่มีการ Control)

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 105

      รูปอ้างอิงที่จะใช้ใน ControlNet

      วิธีการที่จะได้รูปต้นแบบมาเป็นตัวควบคุมนั้นทำได้หลายอย่าง เช่น

      • เอามาจาก internet
      • Gen ขึ้นมาด้วย SD เอง
      • Gen ขึ้นมาด้วย AI ตัวอื่น เช่น MidJourney
      • ใช้เครื่องมือ เช่น Blender3D หรือ https://app.posemy.art/ ในการสร้าง 3D Model ก่อนจะเอาออกมาเป็น Reference ก็ได้

      อันนี้ตัวอย่างว่าเราสามารถกำหนดท่าทางใน https://app.posemy.art/ ได้เลย แล้วอาจเอามาใช้เป็นแบบ สำหรับ OpenPose , Depth, Canny, Normal ก็ได้ (เดี๋ยวตอนใน Model จะเห็นว่าแต่ละอันควบคุมอะไร)

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 106
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 107
      รูปปกติ
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 108
      Canny
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 109
      depth
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 110
      normal
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 111
      openpose
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 112
      openpose+hand

      เอาล่ะ แต่ในตัวอย่างของบทความนี้ ผมจะใช้รูปสมจริงนี้ใน ControlNet โดยใช้ร่วมกับ Prompt ข้างบน

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 113

      เอาล่ะ เรามาดูกันว่าถ้าใช้รูปอ้างอิงข้างต้นเป็นตัวควบคุมในแต่ละ Model จะให้ผลเป็นยังไงในแต่ละ Preprocessor ด้วย

      ควบคุมมนุษย์โดยเฉพาะ

      Model: Openpose

      OpenPose สามารถตรวจจับจุดสำคัญของร่างกายของมนุษย์ เช่น ตำแหน่งของหัว แขน ขา มือ หรือแม้แต่สีหน้าได้ เราจะใช้ Model นี้เพื่อควบคุมท่าทางของมนุษย์ โดยไม่ต้องลอกเลียนรายละเอียดอื่น ๆ เช่น เสื้อผ้า, ทรงผม และพื้นหลัง

      • openpose : = body (ควบคุมร่างกาย) คือ ตา, จมูก, ตา, คอ, ไหล่, ข้อศอก, ข้อมือ, ข้อเข่า, และข้อเท้า
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 114
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 115
      • openpose_face : openpose + รายละเอียดบนใบหน้า
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 116
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 117
      • openpose_faceonly : รายละเอียดใบหน้าอย่างเดียว
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 118
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 119
      • openpose_hand : openpose + มือ (ตำแหน่งนิ้ว)
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 120
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 121
      • openpose_full : openpose + face + hand
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 122
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 123

      Tips: เราสามารถใช้คู่กับ extension https://github.com/huchenlei/sd-webui-openpose-editor เพื่อสามารถกด Edit เพื่อดัดแปลงมนุษย์ก้างที่ Preprocess มาให้ได้ดั่งใจมากขึ้นได้ด้วยนะ (จริงๆ มีหลาย Extension เลย ที่ edit Openpose ได้)

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 124
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 125
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 126

      กลุ่มควบคุมด้วยลายเส้น

      Model: Canny

      ใช้ Canny Edge Detector ในการดึงเอา “เฉพาะเส้นขอบ” ของภาพออกมา ทำให้สามารถสร้างภาพที่มีโครงเหมือนภาพต้นฉบับได้

      • Canny
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 127
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 128

      Model: Scribble

      ควบคุมภาพแบบใช้ลายเส้นที่ชุ่ยๆ เหมือนวาดด้วยเด็กน้อย หรือคนที่วาดรูปไม่เก่ง

      • scribble_hed : Holistically-Nested Edge Detection (HED) เป็นตัวตรวจจับขอบที่ดีในการสร้างเส้นขอบเหมือนคนจริงๆ เหมาะสำหรับการเปลี่ยนสีและการเปลี่ยนรูปแบบภาพ
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 129
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 130
      • scribble_pidinet : ระบบสำหรับตรวจจับเส้นโค้งและเส้นตรงโดยใช้เทคนิคของ Pixel Difference network (Pidinet) ผลลัพธ์คล้ายกับ HED แต่จะได้เส้นที่สะอาดกว่าและมีรายละเอียดน้อยลง (เหมาะสำหรับการคัดลอกเส้นเค้าโครงโดยไม่ต้องคำนึงถึงรายละเอียดละเอียดที่เล็กน้อย)
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 131
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 132
      • scribble_xdog : วิธีการตรวจจับขอบแบบ EXtended Difference of Gaussian (XDoG) เวลาใช้ สามารถปรับค่าเกณฑ์ xDoG เพื่อให้ผลเปลี่ยนไปได้ (เลขเยอะจะทำให้รายละเอียดน้อยลง)

      xDog 1

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 133
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 134

      XDog32

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 135
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 136

      Xdog64

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 137
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 138

      Model: Softedge

      เป็นเหมือนตัวกึ่งกลางระหว่าง Canny กับ Scribble

      มีหลายตัว เช่น

      • softedge_hed
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 139
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 140
      • softedge_pidinet
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 141
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 142
      • softedge_hedsafe
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 143
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 144
      • softedge_pidisafe
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 145
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 146

      แนวทางการใช้งาน

      • คุณภาพผลลัพธ์ : SoftEdge_HED > SoftEdge_PIDI > SoftEdge_HED_safe > SoftEdge_PIDI_safe
      • ความยืดหยุ่น : SoftEdge_PIDI_safe > SoftEdge_HED_safe >> SoftEdge_PIDI > SoftEdge_HED
      • ผู้จัดทำแนะนำให้ใช้ : SoftEdge_PIDI

      Model: MLSd

      MLSD (Mobile Line Segment Detection) เป็นตัวตรวจจับเฉพาะเส้นตรง (โดยไม่สนใจเส้นโค้งหรือเส้นที่ไม่ตรงเป๊ะๆ เลย) มักใช้ในการสกัดเส้นขอบที่มีขอบตรง เช่น การออกแบบอาคาร, ตกแต่งภายใน, ฉากถนน, กรอบรูป, และขอบกระดาษ

      • MLSd
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 147
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 148

      Model: Lineart

      เป็น Model ที่มาใหม่ แต่ทรงพลังมาก มันสามารถแสดงเส้นขอบของภาพ โดยที่เรากำหนดรูปแบบของเส้นให้เหมาะกับรูปต้นฉบับได้

      • lineart_realistic : เส้นสไตล์สมจริง
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 149
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 150
      • lineart_coarse : เส้นสไตล์สมจริงโดยมีความหยาบๆ มากกว่า (จำนวนเส้นจะน้อยลง)
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 151
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 152
      • lineart_standard (from white background and black lines) : ภาพจากเส้นขาวดำอยู่แล้ว (พอเอามาใช้กับภาพปกติ เลยประหลาด เพราะเหมาะกับรูปต้นแบบที่เป็นเส้นอยู่แล้วนั่นเอง)
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 153
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 154

      Model: Lineart Anime

      ออกแบบมาเพื่อใช้ควบคุมภาพสไตล์ anime โดยเฉพาะ (พอเอามาใช้กับ Reference ภาพจริงจะแปลกๆ หน่อยนะ)

      • lineart_anime : เส้นสไตล์อนิเมะ
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 155
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 156
      • lineart_anime_denoise : เส้นสไตล์อนิเมะแบบลดรายละเอียดการควบคุมลง
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 157
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 158

      กลุ่มควบคุมด้วยความลึก/พื้นที่

      Model: Depth

      ควบคุมข้อมูลความลึกของรูปจากภาพอ้างอิง ออกมาเป็นภาพ grayscale

      • depth_midas : ได้เฉพาะความลึกตัวแบบออกมา
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 159
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 160
      • depth_leres : ได้ความลึกตัวแบบและพื้นหลังออกมาด้วย
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 161
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 162
      • depth_leres++ : ได้ความลึกตัวแบบและพื้นหลังออกมาเยอะที่สุด
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 163
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 164
      • depth_zoe : เน้นควบคุม Object ค้อนข้างคล้ายๆ depth_midas
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 165
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 166

      Model: Normal

      ใช้ระบุลักษณะพื้นผิวของ Object คล้ายกับการใช้งาน Depth Map มักใช้ในการถ่ายทอดโครงสร้างสามมิติของภาพต้นแบบ

      • normal_bae : มักได้พื้นผิวทั้ง Object และ Background
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 167
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 168
      • normal_midas : มักได้พื้นผิวของ Object หลักอย่างเดียว
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 169
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 170

      Model: Seg

      Seg ในที่นี้คือ Segmentation หรือการแบ่งกลุ่ม จะทำการบอกประเภทของวัตถุที่อยู่ในภาพอ้างอิงออกมาได้ด้วยสีที่แตกต่างกัน ใช้เพื่อควบคุมตำแหน่งและรูปร่างของวัตถุที่เราต้องการได้แบบเป๊ะๆ

      • seg_ofade20k : การแบ่งกลุ่ม UniFormer (uf) ที่ฝึกสอนด้วยชุดข้อมูล ADE20K
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 171
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 172
      • seg_ofcoco : การแบ่งกลุ่ม OneFormer (of) ที่ฝึกสอนด้วยชุดข้อมูล ADE20K
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 173
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 174
      • seg_ufade20k : การแบ่งกลุ่ม OnFormer ที่ฝึกสอนด้วยชุดข้อมูล COCO
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 175
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 176

      กลุ่มพิเศษ

      Model: IP2P สั่งเปลี่ยนรูปตามต้องการ

      IP2P หรือ Instruct Pix2Pix ทำให้เราสามารถ “สั่ง” ให้รูปผลลัพธ์เปลี่ยนไปได้ตามต้องการ เป็นตัวที่สนุกมากๆๆ ขอบอกเลยครับ โคตรชอบ

      โดยใส่รูปที่ต้องการสั่ง แล้วเลือก Preprocessor เป็น None

      (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2),  make woman very strong and muscular
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 177
      (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2),  (make room on fire:1.4)
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 178

      Shuffle

      ตัวแปรก่อนประมวลผล Shuffle จะทำการ Random หมุนกวนภาพต้นฉบับแบบสุ่ม (ขึ้นกับ seed) สามารถใช้ในการถ่ายทอดแบบสีของภาพอ้างอิงได้

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 179
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 180

      กลุ่ม T2IA

      • t2ia_color_grid : จะควบคุมสีของรูปได้ โดยใช้วิธีลดขนาดภาพอ้างอิงลงถึง 64 เท่าแล้วขยายกลับเป็นขนาดเดิม ผลลัพธ์ที่ได้คือ ผลเป็นแบบตารางที่ประกอบด้วยสีเฉลี่ยของพื้นที่เดิม โดยต้องใช้คู่กับ Model t2iadapter_color
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 181
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 182
      • t2ia_sketch_pidi : คล้ายๆ พวก Model ควบคุมโครงภาพ โดยต้องใช้คู่กับ Model t2iadapter_sketch
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 183
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 184
      • t2ia_style_clipvision : ใช้ทำ Style Transfer ได้ โดยแปลงภาพอ้างอิงเป็น CLIP Vision Embedding การฝังภาพนี้ประกอบด้วยข้อมูลเนื้อหาและสไตล์ของภาพเดิม โดยต้องใช้คู่กับ Model t2iadapter_style
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 185
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 186
      nude เฉยเลย

      กลุ่ม Reference

      ช่วยให้สามารถสร้างภาพที่คล้ายกับภาพอ้างอิงได้

      • reference_only : เลียนแบบรูป Reference
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 187
      • reference_adain : ใช้กับการเลียนแบบ Style จากรูป Reference (Style transfer)
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 188
      • reference_adain+attn : ทำข้างบนทั้งคู่
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 189

      กลุ่มที่มักต้องใช้คู่กับเครื่องมืออื่น

      Model: Tile

      เราใช้อันนี้ใน img2img โดยไม่ต้องใส่รูปใน ControlNet

      • tile_resample : ใช้สำหรับเพิ่มรายละเอียดในภาพ “มักใช้ร่วมกับ Upscaler” (เช่น Script Ultimate Upscale + Model 4x-Ultrasharp) เพื่อขยายภาพให้ใหญ่ขึ้นไปโดยที่ภาพจะไม่ค่อยเพี้ยนจากเดิม แม้จะ denoise ค่อนข้างสูงก็ตาม (ถ้าใส่ down sampling rate เยอะ ภาพจะเปลี่ยนจากเดิมได้มากขึ้น)
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 190
      denoise 0.7 ยังไม่พังเลย
      • tile_colorfix : เหมือน tile resample แต่พยายามทำให้สีไม่เพี้ยนจากต้นแบบ (แม้ว่าจะ conflict กับ prompt) ถ้าเป็นรูปสมจริงอาจะไม่ค่อยเห็นผลนัก
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 191
      • tile_colorfix+sharp : เหมือน tile colorfix แต่พยายามทำให้ภาพคมชัดมาก (ปรับ sharpness ได้)
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 192
      sharpness 1

      Model: Inpaint

      เป็น Model ที่ “ใช้ตอน Inpaint” ซึ่งจำทำให้ได้ผลลัพธ์ที่ดีขึ้นมากๆ

      วิธีใช้งาน Model นี้มี 2 แบบ คือ

      1. ใช้ใน Mode Text to IMG : สามารถใส่รูปต้นฉบับเข้า ControlNet แล้ว Inpaint ในนั้นแล้ว Gen ตามปกติได้เลย รูปจะมีการแก้เฉพาะพื้นที่ที่เรา Inpaint โดยจะไม่มีอิธิพลที่ต้อง denoise จาก img2img มายุ่งด้วยเลย รายละเอียดดูได้ที่คลิปนี้
      2. ใช้ในโหมด Inpaint ของ IMG2IMG : อันนี้รูปต้นฉบับให้ใส่ใน Inpaint ไม่ต้องใส่ใน ControlNet ช่วยให้สามารถใช้ Denoising Strength ที่ค่อนข้างเยอะตอน Inpaint ได้โดย ภาพรวมไม่เละ (เช่น ไม่มีหัวงอก แขนขางอก)

      สมมติ Prompt ตอน inpaint แบบนี้

      (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (jeans:1.4)
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 193
      พื้นที่ Mask
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 194
      Inpaint แบบ Fill Denoise 0.6 แบบไม่เปิด ControlNet
      จะเห็นว่ามีความเพี้ยนเกิดขึ้นพอสมควร
      • inpaint_global_harmonious :
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 195
      • inpaint_only :
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 196
      • inpaint_only+lama : ตัวนี้ผลลัพธ์ค่อนข้างเจ๋งสุดๆ ไปเลย (LaMa คือ Resolution-robust Large Mask Inpainting with Fourier Convolutions เป็น Model ที่ฉลาดเรื่องการ Inpaint มากๆ)
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 197

      Outpainting!

      ตัว inpaint_only+lama มักใช้เพื่อเลียนแบบการ Outpaint ได้ด้วย แต่จะเจ๋งกว่า Outpaint ปกติมากๆ

      prompt คือ

      (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2),( full body shot:1.5), (woman looking at viewer:1.4), casual cloth, ( pants),  in living room, (windows:1.3), (plant and vase:1.2), (lamp :1.4), (cupboard:1.1)

      เปรียบเทียบให้ดูกับ Script ปกติ

      ใช้ Script Outpainting mk2 ในโหมด Img2Img

      ผลลัพธ์เกือบจะดีแล้ว แต่มีความเพี้ยนตรงรอยต่อจากภาพเดิม เช่น ขอบโซฟา เป็นต้น

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 198

      ถ้าจะใช้ inpaint_only+lama

      วิธีที่ 1: ใช้ Txt2Img สามารถใช้โหมด Txt2Img แต่ใส่รูปต้นแบบใน ControlNet กำหนดขนาดผลลัพธ์ตามต้องการได้เลย แล้วให้เลือก ControlNet Mode เป็น Resize and Fill และ ControlNet is more important

      เราจะพบว่าผลลัพธ์นั้นดูดีกว่าเดิมมาก

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 199

      วิธีที่ 2 ใช้ img2Img : ทำเหมือนกันเลย แต่ใส่รูปใน Img2Img ด้วย แล้วปรับค่า Denoise เช่นได้ตามใจชอบ (แนะนำ 0.6 ขึ้นไป และ สามารถใส่สูงถึง 1 ได้เลย)

      แล้วใน ControlNet ก็ใส่รูปต้นแบบด้วย (ใส่รูปต้นแบบทั้ง 2 ที่) แล้วให้เลือก ControlNet Mode เป็น Resize and Fill และ ControlNet is more important เช่นกัน

      ผมลองปรับ Denoise ให้ดูหลายๆ ค่า (ยิ่ง Denoise น้อย จะยิ่งคล้ายๆ พื้นที่เดิม)

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 200
      denoise 0.6
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 201
      denoise 0.8
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 202
      denoise 1

      ผลออกมาได้เทพมาก! ผลลัพธ์พอๆ กับใช้ Generative Fill ของ Photoshop Beta ที่เสียตังแพงๆ เลย แต่ใน SD ใช้ฟรี!!

      การประยุกต์

      จริงๆ พอเรารู้ว่ามันควบคุม Object ด้วยวิธีต่างๆ ที่ผมบอกข้างบนแล้ว จริงๆ เราก็สามารถเอาภาพที่ Preprocess ได้มาดัดแปลง ปรับปรุงให้เข้าทางเราได้มากขึ้น

      ตัวอย่าง : Segmentation

      เช่น ใน Segmentation เราใช้สีที่แตกต่างกันในการกำหนด Object ได้ แบบนี้เราก็สามารกำหนดได้เลยว่าจะมี Object อะไรอยู่ตรงไหน (แต่ถ้าให้ดีควร Prompt ช่วยด้วย)

      เช่น ผมจะเอาพืชไปพันรอบตัวผู้หญิง ผมก็สามารถใช้ Photo Editor อย่าง Photoshop หรือ Photopea ไปวาดรูปเพิ่มให้สีของพืชพันตัวผู้หญิงในตำแหน่งที่ต้องการได้เลย

      (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (beautiful woman wearing jeans:1.4), (get tied by plant vine:1.4)
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 203
      ไปวาดรูปเพิ่มใน Photo Editor ให้สีของพืชพันตัวผู้หญิง
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 204
      เราก็จะควบคุมผลลัพธ์ได้ดีขึ้น

      นอกจากนี้เรายังประยุกต์ในการดัดแปลงภาพโครงลายเส้นต่างๆ ให้มีสิ่งที่เราต้องการได้เช่นกันด้วยการวาด หรือตัดต่อเข้าไป

      ตัวอย่าง : Scribble

      อันนี้ใช้ Scribble แล้วไปดัดแปลงใน Photopea เพื่อเปลี่ยนรูปให้ได้ดั่งใจ

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 205
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 206

      ตัวอย่าง Multi-ControlNet

      เราสามารถใช้ ControlNet หลาย Model ร่วมกันได้ ซึ่งเราสามารถเล่นอะไรแปลกๆ ได้ตามใจชอบ ซึ่งในที่นี้ผมขอใช้ตัวที่ไม่ conflict กันมากนัก

      OpenPose + Shuffle

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 207

      OpenPose + TiAdapter Color

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 208

      MLSD + TiAdapter Color

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 209

      MLSD+Shuffle

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 210

      ลอง Mix & Match เล่นๆ : ใช้ Preprocessor ที่ไม่ใช่ของ Model

      ใช้ Segment ใน TiColor

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 173
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 212

      ใช้ TiColor ใน Model Segmentation

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 181
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 214

      ใช้ TiColor ใน Tile

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 181
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 216

      ใช้ OpenPoseใน Scribble

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 114
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 218

      ใช้ OpenPoseใน Soft Edges

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 114
      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 220

      ความ Creative ไม่มีที่สิ้นสุด!!

      Unofficial Model

      สร้าง QR Code

      ใช้สร้างรูปที่ใช้ QR Code เป็น Reference เพื่อให้สามารถ Scan ในฐานะเป็น QR Code ได้ ซึ่งมี 2 แนวทางที่ใช้ Model ต่างกัน แต่ก่อนอื่นเรามาเตรียมรูป QR ก่อน

      อันนี้รูป QR Code ต้นแบบ

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 221

      (สร้างจากที่นี่ https://dnschecker.org/qr-code-generator.php
      โดยให้ย่อ Link ให้สั้นก่อน แล้วค่อยเอาไปทำ QR
      โดยเลือก Error Correction เป็น H=High)

      วิธีที่ 1 ใช้ Brightness Model

      ControlNet Brightness Model (ควบคุมความสว่าง) : ไปโหลดที่นี่

      อันนี้ผมใช้ Brightness (ปรับ Weight ให้เหมาะ) และปรับ Model Resize & Fill (ผม Gen ภาพ 512×768) และ Control Start & End ให้ดี

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 222

      วิธีที่ 2 ใช้ ControlNet QR Code Model

      ControlNet QR Code Model : (ใช้ทำ QR โดยเฉพาะ) ต้องไปโหลดเพิ่มที่นี่ (โหลดทั้ง model และ yaml)

      อันนี้ผมใช้ Model QR Code ซึ่งเท่าที่ลอง มีประเด็นสำคัญดังนี้

      • ต้อง Gen รูปเป็นจตุรัส เช่น 768×768 ถึงจะ Work
      • ต้องปรับ Control Weight ให้มากกว่า 1 เช่น 1.6 ขึ้นไปเลย ไม่งั้นจะ Scan ไม่ติด
      • เลือก Preprocessor เป็น Invert จะได้ลายสีดำ (ค่าปกติจะได้ลายสีขาว)

      นอกจากนี้เรายังสามารถ พลิกแพลงใช้ร่วมกันหลายๆ อย่าง เช่น ผม Gen รูปนี้ออกมาก่อนด้วย Txt2img ปกติ โดยยังไม่ Control อะไรเลย

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 223

      จากนั้นลองเอารูปที่ได้ไปใช้เป็นตัว Control Model ControlNet อื่นๆ ที่จะใช้ Gen คู่กับ ControlNet QR Code (ในที่นี้ผมใช้ Openpose ช่วยคุมอีกก็ได้)

      ผลของผมที่ออกมาได้แบบนี้

      เจาะลึก ControlNet ใน Stable Diffusion [Part8] 224

      ซึ่ง iphone ของผม scan ติดด้วยนะ 5555

      สรุป

      เห็นรึยังว่า “ControlNet โคตรทรงพลัง!” ดังนั้น.. หัดใช้ให้เป็นเถอะครับ!!

    • วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7]

      วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7]

      หากเพื่อนๆ ใช้ Stable Diffusion ทำการ Gen รูปขึ้นมาซักพัก คุณคงรู้ว่าสิ่งหนึ่งที่สำคัญมากๆ ที่จะทำให้ภาพผลลัพธ์ออกมาสวยงามก็คือตัว Model ที่เราเลือกใช้ในการ Generate รูปนั่นเองครับ

      แม้ว่าในตอนที่ 2 ผมจะได้พูดถึงวิธีการโหลด Model ใหม่ๆ ไปแล้ว แต่มันจะดีกว่ามั้ย ถ้าเราสามารถผสม Model ขึ้นมาใช้เองแบบโดนใจเราสุดๆ ได้? และมันอาจจะสามารถใช้เพื่อที่เราจะได้สร้างรูปที่ดูมีความต่างจากงานของคนอื่นได้ด้วยนะ (อันนี้ดีมากๆ)

      ซึ่งในบทความนี้ผมจะมาสอนทุกคนทำการผสม Model Checkpoint ขึ้นมาใช้เองกันครับ

      ขอเตือนก่อนว่า บทความนี้ถูกเขียนในสไลต์ที่ไม่เหมือนกับบทความก่อนๆ ของผมเลย เพราะมันไม่ได้เป็นการสอนมากนัก แต่เป็นการทดสอบให้ดูซะมากกว่า และเนื้อหาบางส่วนก็อาจดู Nerd สุดๆ ไปเลย ดังนั้นจะเหมาะกับคนที่อยากลงลึกด้านผสม Model โดยเฉพาะครับ

      รวมบทความ Stable Diffusion

        การผสมมีแบบไหนบ้าง?

        ซึ่งผมจะแนะนำการผสม Model Checkpoint 2 แบบ คือ แบบ Basic ทั่วๆ ไป กับแบบ Merge Block Weight ที่จะเป็นแบบ Advance ครับ ซึ่งบอกไว้ก่อนเลยว่าถ้าใครจะทำตามก็ต้องการพื้นที่ Harddisk พอสมควรเลย เพราะ Checkpoint 1 ไฟล์ก็ประมาณ 5GB แล้ว หึหึ…

        โดยผมจะใช้ Model Checkpoint ตัวอย่าง 2 อัน ที่ต่างกันโดยสิ้นเชิงมาผสมกันนั่นคือ

        หมายเหตุ : จริงๆ คุณจะเอา Checkpoint ไหนมาผสมกันก็ได้ ยิ่งทดลองยิ่งสนุก!

        โดยเอาทั้ง 2 Model ไปไว้ใน Folder stable-diffusion-webui\models\Stable-diffusion ให้เรียบร้อยก่อนนะครับ

        ผสม Model แบบ Basic

        สำหรับการผสมแบบ Basic เราจะใช้เครื่องมือที่เรียกว่า Checkpoint Merger ที่มีมากับ Automatic1111 อยู่แล้วเลยครับ

        ผสมแบบ Weighted SUM

        โดยที่เราจะเลือกแต่ละ Model ขึ้นมาที่ Model A กับ B แล้ว เลือกอัตราส่วน Multiplier (M) ที่จะผสมซึ่งมีความหมายดังนี้

        คือสูตรการผสมคือ A * (1 – M) + B * M
        ดังนั้น Multiplier ก็คือ มองได้ว่า คือ อัตราส่วนของ B นะไม่ใช่ A

        • ใส่ 0 คือไปทาง A 100%
        • ใส่ 0.3 คือ ไปทาง A 70% (อันนี้เห็นชัด)
        • ใส่ 0.5 คือ ผสมอย่างละครึ่ง (50%)
        • ใส่ 1 คือ B 100%

        Interpolation Method ผมเลือกแบบ Weighted Sum ก็คือให้มันเอามารวมแบบเฉลี่ยกัน และบันทึกไฟล์แบบ safetensor

        แล้วผมจะตั้งชื่อไฟล์แบบนี้คือ ChillOut75MeinaMix25
        แปลว่า มีChillOut75% Meina25%
        ซึ่งเกิดจากการตั้ง ChillOut เป็น Model A โดยมี Multiplier เป็น 0.25 นะครับ

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 225

        พอกด Merge แป๊ปเดียวก็จะเสร็จเลย ไม่นานครับ (ไม่ถึง 1 นาที)

        แล้วผมทำอย่างนี้อีก 2 อัตราส่วน คือ

        • ChillOut50MeinaMix50 (Multiplier 0.5)
        • ChillOut25MeinaMix75 (Multiplier 0.75)

        พอได้ไฟล์ครบแล้วเราจะมาทดสอบผลของการ Merge Model กันครับว่าออกมาเป็นยังไง

        ทำการทดสอบ

        ในการทดสอบ เราจะใช้ x/y/z plot มาช่วย โดยเลือกให้เปลี่ยน checkpoint เป็น 5 ตัว คือ Chillout100%(MeinaMix 0%), 75% 50% 25%, 0%(MeinaMix 100%) ตามลำดับ

        หมายเหตุ : ใน x values พยายามอย่ากดขึ้นบรรทัดใหม่นะ ไม่งั้นมันจะเอ๋อ (สร้างรูปเกิน)

        เช่น ผมใส่แบบนี้

        chilloutmix_NiPrunedFp32Fix.safetensors [fc2511737a],ChillOut75MeinaMix25.safetensors [589c49a4e3],ChillOut50MeinaMix50.safetensors [ab59bbccc8],ChillOut25MeinaMix75.safetensors [49a76cdc3a],meinamix_meinaV9.safetensors [eac6c08a19]
        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 226

        ในบทความนี้ผมจะใช้ Negative Prompt เหมือนกันทุกครั้ง และเป็นคำกลางๆ ที่ใช้ได้ทั้งภาพสมจริงและภาพการ์ตูน คือ

        (worst quality:2), (low quality:2), (lowres, normal quality:2)

        และจะใช้ Sampling Method ผมใช้ DPM++SDE Karas, Steps 20, CFG 7 และ จะใช้ seed 1234 เท่ากันหมดก่อน เพื่อความ simple

        และจะเปลี่ยน Prompt และ Model ไปเรื่อยๆ (แต่จะใส่คำกลางๆ เพื่อให้ใช้ได้ทุก Model) เช่น ตอนแรกจะใช้ Prompt นี้

        (best quality:1.4), (ultra highres:1.2), highest quality, highres, 1girl, (beautiful thai girl), (long hair), (in red tank top, jeans),(sit on the sofa)
        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 227

        ปรากฏว่าภาพออกมา จะเห็นว่าภาพคล่อยๆ เปลี่ยนจากภาพสมจริงของ Chillout100% มาเป็นภาพการ์ตูนของ Meinamix 100% ตามน้ำหนักที่เราใส่ไว้นั่นเอง

        ลองเปลี่ยน Prompt ที่ทำให้ 2 Model ต่างกันมากหน่อย

        ผมจะพยายามทำให้ภาพทั้ง 2 ฝั่งอาจต่างกันพอสมควร เช่น ท่าทาง มุมกล้อง รายละเอียดฉากหลัง เพื่อที่จะได้เห้นอะไรชัดเจนขึ้นเวลา Merge Model แล้ว

        โดยที่ผมทำ prompt ไว้ 4 ชุด แต่ละชุด Save เป็น Styles เอาไว้ก่อน (กดรูป icon Save ที่ใกล้ๆ ปุ่ม Generate) จะได้เรียกใช้ง่ายๆ

        Style = S1_BASKETGIRL

        (best quality:1.4), (ultra highres:1.2), highest quality, highres, 1girl, (beautiful thai girl),looking at viewer, (short hair), (in pe uniform), (in basketball stadium:1.1)

        Style = S2_GAMERGIRL

        (best quality:1.4), (ultra highres:1.2), highest quality, highres, 1girl, (beautiful thai girl:1.3),(looking at viewer:1.4), (portrait shot), (ponytail hair), (gaming headphone), (high tech gaming computer:1.3), (RGB, neon color light effect:1.2), (in gaming bed room in background)

        Style = S3_COFFEEGIRL

        (best quality:1.4), (ultra highres:1.2), highest quality, highres, (beautiful thai girl), (as a coffee barista:1.2), (casual cloth, apron:1.1), (in coffee shop:1.3)

        Style = S4_KNIGHTGIRL

        (best quality:1.4), (ultra highres:1.2), highest quality, highres, (beautiful thai girl), (using two hand knight sword:1.4),(in Europe middle age full plate knight armor:1.3), ( in the battle with large group of enemy army:1.4)

        แล้วผมจะใช้ xyzplot เปลี่ยนทั้ง Checkpoint Name และ Style พร้อมกันเลย (Negative เดิม) เพื่อที่จะได้เห็นภาพทุกแบบพร้อมๆ กันง่ายๆ แบบนี้

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 228

        ซึ่งจะ Gen รูปออกมาได้แบบนี้เลยทีเดียว

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 229

        ก็จะเห็นว่า กรณีที่ผลของทั้ง 2 ฝั่งต่างกันพอสมควร ทั้งมุมกล้องก็ต่าง รายละเอียด accessory ต่างๆ เวลา Merge ระหว่างทาง ผลมันก็สามารถเกิดความแปลกใหม่มากๆ เลย…

        ดังนั้นแปลว่า Model แต่ละอัน สามารถมีวิธีการตีความ Text Prompt ของเราแตกต่างกัน เช่น จะวาดอะไรบ้าง ตรงไหน แล้ววิธีในการสร้างภาพที่ต่างกันด้วย เช่น จะวาดไสตล์ไหน

        ทั้งนี้ ถ้าเป็นการ Merge แบบ Basic ก็ง่ายๆ แค่นี้แหละ หลังจากนี้เราก็สามารถเลือกอัตราส่วนที่เราชอบไปใช้ได้เลย รวมถึงยังสามารถเอาไปผสมกับ Checkpoint อื่นต่อได้อีกเรื่อยๆ ไม่มีที่สิ้นสุดครับ

        แต่ถ้าจะผสม 3 Model แบบ Weighted SUM เราไม่สามารถเลือก Model A, B, C แล้วผสมพร้อมกันด้วยเครื่องมือปกติได้ เพราะ Weighted SUM จะใช้สูตร A * (1 – M) + B * M ซึ่งไม่เกี่ยวกับ C เลย

        ส่วนถ้าใช้ C จะเป็นกรณี Add Difference ซึ่งอันนั้นทำงานอีกแบบนึง คือ A + (B – C) * M

        ผสมแบบ Add Difference

        การผสมอันนี้ถึงจะมีการใช้ Model C นะ โดยผมใส่ Model C เป็น Model การ์ตูนอันนี้ https://civitai.com/models/4468/counterfeit-v30

        ลองไล่ตามนี้

        ChillOut | Meina | Counterfeit | M0.25 | M0.50 | M0.75 | M1.00

        ภาพที่ได้ออกมาแบบนี้ (ขอ Censor หน่อยนะ ผสมแล้วมันโป๊เฉยเลย 555)

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 230

        ซึ่งผลของการใช้ Add Difference ค่อนข้างทำความเข้าใจยาก แต่ตามหลักแล้วคือ หากว่า Model B กับ C ต่างกันตรงไหน แล้ว Add ความต่างนั้นเข้าไปใน A ครับ

        สรุปแบบ Basic

        ซึ่งถ้าสังเกตจะเห็นว่าการผสม Model แบบ Weighted Sum (Basic) มันก็จะเอา Model A กับ B มาผสมๆ กันในภาพรวมตามอัตราส่วนหรือน้ำหนักที่กำหนด แบบที่ควบคุมเฉพาะบางเรื่องไม่ได้

        แต่จะใช้แบบ Add Difference ก็ควบคุมได้ยากอยู่ดี…

        แต่ถ้าเราอยากจะกำหนดแบบเจาะจงว่าอยากเอาอะไรของ Model A มาผสมกับอะไรของ Model B อันนี้ต้องผสมแบบ Advance แล้ว ถ้าสนใจก็อ่านต่อได้เลย!

        ผสม Model แบบ Advance

        สำหรับการผสมแบบ Advance เราจะใช้ Extension Merge Block Weighted https://github.com/bbc-mc/sdweb-merge-block-weighted-gui มาช่วย ซึ่งมันมีความสามารถในการผสม Model แบบกำหนดได้อย่างละเอียดเลยว่า ส่วนผสม Block ไหนจะใช้ของ Model ตัวไหน ซึ่งจะมีประโยชน์คือ สามารถพลิกแพลงให้เกิดภาพผลลัพธ์ที่ Merge ปกติทำไม่ได้ (หรือยาก) เช่น ทำให้โครงสร้างของรูปมาจาก Model A แต่ว่ารายละเอียดวิธีวาดมาจาก Model B เป็นต้น

        หมายเหตุ : จริงๆ มี Extension อีกตัว คือ SuperMerger ( https://github.com/hako-mikan/sd-webui-supermerger.git ) ซึ่งตัวนี้จะปรับแต่งได้เยอะมาก แต่ผมกลัวคนอ่านจะงง ขอเอาตัว Weight ปกติให้ดูก่อนนะ ไว้ตอนต่อๆ ไปจะเล่น Super Merger ให้ดูอีกที

        บอกก่อนว่าสำหรับการผสมแบบ Merge Block นี้ผมเองก็ยังไม่ได้รู้อะไรลึกมาก แต่จะขอ “อาสาช่วยทำการทดสอบ” ลองผสม Model แล้ว Gen ออกมาให้ดูหลายๆ แบบ แล้วให้เพื่อนๆ ช่วยกันสังเกตแล้วกันว่ามันทำงานยังไงกันแน่ ถ้าใครได้ข้อสรุปอะไรดีก็ฝากบอกด้วยนะ

        ก่อนอื่นให้ไปลง Extension ให้เรียบร้อยก่อน แล้วจะมี Tab Merge Block Weighted โผล่ขึ้นมา

        แต่มันจะมีอะไรให้เลือกเยอะแยะไปหมดเลย อย่าเพิ่งตกใจ เราจะค่อยๆ มาทำความเข้าใจกระบวนการ Gen รูปกันให้ละเอียดขึ้นก่อน

        ทบทวนกระบวนการ Gen รูปในเชิงเทคนิค

        ภาพนี้สรุปกระบวนการ Gen รูปของ Stable Diffusion ไว้ดีมาก

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 231
        https://jalammar.github.io/illustrated-stable-diffusion/
        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 232
        https://jalammar.github.io/illustrated-stable-diffusion/

        เราจะพบว่ากระบวนการที่เปลี่ยน Noise ให้เป็นรูปนั้น จะค่อยๆ เปลี่ยนทีละ Step และในแต่ละ Step ก็จะมีสิ่งที่เรียกว่า U-Net ในการช่วยเปลี่ยนรูปที่มี Noise ให้เป็นรูปที่สมบูรณ์ (Noise น้อยลง) มากขึ้นเรื่อยๆ

        ซึ่ง U-Net แต่ละตัว นั้นมีโครงสร้างประมาณนี้ (ซึ่งตามโครงสร้างแล้วมันเหมือนรูปตัว U ก็เลยเรียกว่า U-Net ไง)

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 233
        รูปจาก https://brunch.co.kr/@advisor/43

        อะไร คือ In-Out??

        ก่อนอื่น เจ้า In-Out พวกนี้คือส่วนหนึ่งของสิ่งที่เรียกว่า U-Net (ผู้สร้างรูป)

        โดยอยากให้ลองดูภาพนี้ก่อนว่า In-Out แต่ละตัว ความหมายคร่าวๆ หมายถึงอะไร?

        ถ้าไล่จากซ้ายไปขวา

        • ช่วง In คือ การพยายามบีบอัด (Down Sampling) ให้ภาพอยู่ในรูปแบบที่เล็กลง (ตรงกลาง) นั่นคือมันพยายามเก็บข้อมูลจากรูปใน Step ก่อนหน้า
        • Middle (M00) คือ ข้อมูลที่ถูกเก็บไว้จนอยู่ในขนาดที่เล็กที่สุด
        • ช่วง Out ทำการแปลงรูปขึ้นมาใหม่จนใหญ่และละเอียดขึ้น (Up Sampling) ในตอนหลัง ซึ่งมองได้ว่าขั้นตอน Out มีหน้าที่หลักในการสร้างรูปขึ้นมานั่นเอง

        สิ่งสำคัญคือ Step การทำงานมันวิ่งจากซ้ายไปขวาแล้วผ่านทุก Block นั่นคือ
        Block IN00 -> IN01 -> IN02 -> … -> IN11 -> M00 -> OUT00 -> OUT01 -> OUT11

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 234
        ภาพจาก https://rentry.org/BlockMergeExplained

        ถ้าเรา Gen ภาพ 20 Steps แปลว่า มันผ่าน U-Net แบบนี้ 20 รอบ แต่ละรอบจะค่อยๆ มี Noise น้อยลงเรื่อยๆ นั่นเอง

        อะไรคือ Base Alpha

        Base_Alpha คือตัวตัดสินใจว่าจะใช้ Text Encoder ของ Model ไหน โดยที่มันคล้ายๆ กับเป็นคนแปลข้อมูลจาก Text Prompt, Tag ต่างๆ แล้วไปอธิบายให้กับนักวาด (U-Net) อีกที

        • 0 = ใช้ Model A 100%
        • 1 = ใช้ Model B 100%
        • ถ้าใส่เป็นอัตราส่วน ก็จะใช้ text encoder ผสมกัน (ผมลอง 0.5 ให้ดู)

        ซึ่งในทางเทคนิค ข้อความใน Text Prompt จะถูกแปลงเป็น Text Embedding แล้วเอามาใช้ในการเหนี่ยวนำ Noise ที่เกิดขึ้น (เรียกว่าใส่ Attention เข้าไป) เพื่อให้มันสร้างรูปที่ตรงกับ prompt ที่เราสั่งอีกที คล้ายๆ แบบนี้

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 235
        https://jalammar.github.io/illustrated-stable-diffusion/

        เอาล่ะ พอเข้าใจภาพรวมแล้ว เรามาลองทำสอบกันว่าถ้าเปลี่ยน Base Alpha (Text Encoder) จะเกิดอะไรขึ้น?

        ก่อนอื่น ผมจะลองผสม ChillOut กับ Meina แบบ 50:50 นั่นคือไม่ปรับ in-out เลย ให้มันเป็น 0.5 ทั้งหมด (กด Clear Values ได้)

        และเลือก Base Alpha เป็น 0, 0.5 และ 1 ดูก่อนว่าผลเป็นยังไง (ในรูปเลือก 0)

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 236

        จากนั้น ลองผสมเพิ่มแบบให้ base alpha ตรงข้ามกับตัวปกติ ซึ่งเราเลือก Preset ได้นะ ไม่ต้องไปเลื่อน in-put เอง

        • ผสม Model A กับ B แต่ให้มาทาง A100% (preset = All_A) แต่ Base Alphaเป็น 1 (กลับทิศ)
        • ผสม Model A กับ B แต่ให้มาทาง B100% (preset = All_B) แต่ Base Alphaเป็น 0 (กลับทิศ)
        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 237

        ลองใช้ xyz plot แบบนี้ คือลองทั้ง base alpha 0 กับ 1 สลับกันไป

        chilloutmix_NiPrunedFp32Fix.safetensors [fc2511737a],ChillOut100MeinaMix0Alpha1.safetensors [8f99e17e7a],ChillOut50MeinaMix50Alpha0.safetensors [625d2d6c2e],ChillOut50MeinaMix50Alpha05.safetensors [9b6fa7bf57], ChillOut50MeinaMix50Alpha1.safetensors [589ccfafc8],ChillOut0MeinaMix100Alpha0.safetensors [afeb25b6f7],meinamix_meinaV9.safetensors [eac6c08a19]

        เอามา Gen รูป จะได้แบบนี้

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 238

        ซึ่งเอาตามตรง ผมก็ยังไม่เข้าใจ effect ของ base alpha มากนัก แต่ที่เห็น คือ

        • การใช้ base alpha 0.5 และทุกอย่างทั้ง in และ out เป็น 0.5 หมด จะได้ผลเหมือนกับ Basic Merge ที่ weight 0.5
        • การเลือกเอาทาง Model ซักอัน 100% แต่เอา base alpha จากอีก Model นึง ก็ได้ผลลัพธ์ที่เปลี่ยนไปเช่นกัน
        • ดังนั้นแค่ base alpha ที่เปลี่ยนไปก็มีผลต่อรูปที่ออกมาแล้ว ก็เป็นเรื่องที่น่าทดลองเล่นดูนะครับ (หลายๆ อันก็ออกมาน่าสนใจมาก)

        ลองปรับ IN-Out แบบละเอียด

        ที่นี้ความหมายของแต่ละ block นั้นคืออะไร? ก็มีคนพยายามจะทำการทดสอบและทำความเข้าใจ ซึ่งเค้าก็บอกกันว่ามันประมาณนี้

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 239
        จาก https://rentry.co/Merge_Block_Weight_-china-_v1_Beta
        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 240

        ซึ่งจะเป็นจริงรึเปล่า ผมจะลองทดสอบให้ดูด้วยว่าผล (ในกรณีของผม) เป็นยังไงครับ

        ลองใช้ Preset ที่มีอยู่แล้ว

        แม้จะบอกว่าปรับละเอียด แต่เราสามารถจะเลือกใช้ Preset ที่มีมาให้ได้ครับ ความหมายของ Preset แต่ละตัวดูได้ที่นี่ (สีน้ำเงิน =Model A , สีแดง =Model B)

        ผมขอลองนำเสนอ ตัวที่เรียกว่า กับ WRAP16 ให้ดูครับ

        WRAP16

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 241

        สมมติผมเลือก Preset Wrap16 แล้วใส่ A=ChillOut, B=MeinaxMix, Base alpha เป็น 0 ด้วยจะได้แบบนี้ (ผมตั้งชื่อ model ว่า ChillOutMeinaMixWrap16_Alpha0 )

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 242

        พอเลือก Preset Wrap16 แล้ว Slider จะเปลี่ยนโดยอัตโนมัติ ตามรูป diagram ข้างบน (ผมเอามาแปะให้ดูจะได้ชัดๆ)

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 243
        ถ้าเราเลือก WRAP16 แบบนี้ แล้วลองเปลี่ยน base alpha ให้ดูทั้ง 0, 0.5,1

        โดยเปลี่ยน checkpoint แบบนี้

        chilloutmix_NiPrunedFp32Fix.safetensors [fc2511737a],ChillOutMeinaMixWrap16Alpha0.safetensors [245b5ab42e],ChillOutMeinaMixWrap16Alpha05.safetensors [c17d50da91],ChillOutMeinaMixWrap16Alpha1.safetensors [107b4c09c6],meinamix_meinaV9.safetensors [eac6c08a19]

        ผลที่ได้เป็นแบบนี้ คือจะเห็นว่าแทบจะสลับ Composition กับรายละเอียดวิธีวาดภาพกันเลย (แต่รูป Gamer ไม่เห็นผลเท่าไหร่)

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 244
        ทดสอบ Rev_SmoothStep*2

        คราวนี้ ผมลองใช้ Rev_SmoothStep*2 ว่าถ้าเป็นรูปแบบคล้ายกัน แต่มีการไล่ Weight ระหว่างทางด้วยผลจะเป็นแบบไหน?

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 245
        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 246

        จะได้ผลแบบนี้ ซึ่งผมมองว่ามีการผสมผสานบางอย่างกันมากขึ้น

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 247

        จะเห็นว่า พอใช้ preset ที่มีการไล่ weight ภาพมันจะดูผสมผสานระหว่าง 2 model มากขึ้น เช่นในเคสที่เป็นภาพสมจริง + anime ผสมกันก็จะดูเป็น 2.5D มากขึ้น ถ้าใครชอบแบบนี้ก็น่าจะเล่นพวก preset ที่มีการไล่ weight ครับ

        ทำการทดลองปรับ In-Out ทีละส่วน

        หากเราปรับ In Out หลายเรื่องพร้อมกันมากเกินไป เราก็จะไม่เข้าใจว่าจริงๆ แล้วแต่ละอันมันทำอะไรกันแน่… ดังนั้นเราจะมาลองค่อยๆ ปรับทีละน้อยๆ ลงหน่อย ซึ่งมันมีทั้งหมด 25 block (ฝั่งละ 12×2 ฝั่ง + กลางอีก 1) ดังนั้นผมจะปรับทีละ 4 block แล้วกัน เพื่อที่จะได้ทำความเข้าใจได้มากขึ้น (ใครขยันจะลองทำทีละ Block เลยก็ได้นะ…)

        ซึ่งเราจะทดลองให้ทุก Parameter เป็น 0 ให้หมด (คือเป็น A 100%) แล้วค่อยๆ ปรับทีละส่วนเป็น B 100% โดยที่ผมจะใช้ base alpha เป็น 0.5 แล้วกัน จะได้ไม่ต้อง Gen เยอะมากจนเกินไป (เพราะมันจะดูกลางๆ ระหว่าง 0 กับ 1) เอาล่ะ เริ่ม!

        ปรับช่วงปลายทั้งสองข้างของ U-Net IN 00-03 และ OUT 08-11 เป็น B 100%

        ผมมีลองใส่ M00 ไปด้วยอีกอัน ดูว่ามีผลหรือไม่ เรียงดังนี้

        ChillOut | IN 00-03 | Out 08-11 |
        IN 00-03 และ Out 08-11 | IN 00-03 และ M00 และ Out 08-11 | MeinaMix

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 248

        สรุปความเห็น

        เท่าที่ดูแล้ว โดยรวม ค่าช่วงปลายทั้งสองข้างของ U-Net (IN 00-03 และ OUT 08-11) ไม่เกี่ยวกับ Composition แน่นอน เพราะถึงแม้จะปรับเป็น B แล้ว ภาพยังค่อนข้าง Compose เหมือน Model A อยู่จริงๆ

        ดังนั้นที่มีคนบอกว่าช่วงปลายทั้งสองข้างของ U-Net นี้มันเกี่ยวกับราบละเอียดของภาพก็น่าจะมีความถูกต้องอยู่

        ปรับช่วง IN 04-07 และ OUT 04-07 เป็น B 100%

        ChillOut | IN 04-07 | Out 04-07 |
        IN 04-07 และ Out 04-07 | IN 04-07 และ M00 และ Out 04-07 | MeinaMix

        กรณี Base Alpha เป็น 0

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 249
        กรณี Base Alpha เป็น 0.5
        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 250
        กรณี Base Alpha เป็น 1
        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 251

        ปรับช่วง IN 08-11 และ OUT 00-03 เป็น B 100%

        มันก็คือส่วนกลับของ Wrap16 นั่นเองครับ มาลองกัน

        ChillOut | IN 08-11 | Out 00-03 |
        IN 08-11 และ Out 00-03 | IN 08-11 และ M00 และ Out 00-03 | MeinaMix

        กรณี Base Alpha เป็น 0
        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 252
        กรณี Base Alpha เป็น 0.5
        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 253

        แบบพิสดาร : กลุ่ม SmoothStep/3

        ลองใช้ Preset = SmoothStep/3 และ Reverese Smooth Step/3 ดูครับ

        ChillOut | SmoothStep/3 | Reverse SmoothStep/3 | MeinaMix

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 256

        แบบพิสดาร : กลุ่ม True Cubic Hermite

        ใช้ Preset = True Cubic Hermite และ True Reverse Cubic Hermite

        ChillOut | True Cubic Hermite | True Reverse Cubic Hermite | MeinaMix

        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 259

        ปรับช่วง OUT ทั้งหมด เป็น B 100%

        คราวนี้เรามาทดสอบ IN เป็น A แต่ Out ทั้งแถบเป็น B กันบ้าง

        ซึ่งจะคล้ายกับ Preset OUT12 กับ OUT12_5 แต่ผมจะทำให้ M00 เป็น B100% เพิ่มอีกอัน

        กรณี Base Alpha เป็น 0
        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 262
        กรณี Base Alpha เป็น 0.5
        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 263
        กรณี Base Alpha เป็น 1
        วิธีผสม Model สูตรผสมแบบโดนใจใน Stable Diffusion [Part7] 264

        ข้อสรุปแบบ Block Merge

        เป็นการสรุปจากการสังเกต (จะมาเพิ่มเรื่อยๆ ถ้าผมได้ข้อสรุปที่ค่อนข้างชัดเจน เพื่อนๆ ก็ช่วยสังเกต ช่วยทดสอบ แล้วมาบอกกันได้นะ ^^)

        • IN เลขเยอะ เช่น IN08-11 เกี่ยวข้องกับ Composition โดยรวม
        • Out ช่วงกลาง เช่น Out04-07 เกี่ยวข้องกับการวาด subject หลัก เช่นตัวละคร

        Resources

      • สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6]

        สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6]

        ในตอนที่แล้วเราได้สามารถเทรนหน้าตัวเองเป็น LoRA ได้แล้ว ในตอนนี้เราจะลองเอาหน้าตัวเองมา Gen ยืนคู่กับสาวด้วย Stable Diifusion กันครับ หรือจริงๆ บทความนี้ก็คือการสอน Gen ตัวละครมากกว่า 1 ตัวแบบเรากำหนดหน้าตาแต่ละตัวได้นั่นเองครับ ซึ่งเราจะมีการใช้เครื่องมือ Latent Couple และ Composable LoRA มาช่วยด้วยครับ

        สมมติว่าตัวผมเองอยากยืนคู่กับ Lucy ใน Cyber Punk Edge Runner ที่ชายหาด ซึ่งเราสามารถไปโหลด LoRA ของ Lucy มาได้ที่นี่

        รวมบทความ Stable Diffusion

          ลอง Generate แบบตรงๆ

          หากเราพยายามจะ Generate รูปแบบตรงไปตรงมา เช่น เขียนว่าต้องการ ผู้ชาย 1 คน ผู้หญิง 1 คน แต่ละคนเป็นยังไง…

          prompts

          (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (Beautiful beach and blue sky), (1man+1girl),
          
          (SiraEkabut Man with red brown hair), (smile), (slim body), (white collar shirt:1.3) <lora:SiraEkabutV4-crop768-512-128-50-clip1-chillout-000003:0.71>,
          
          (lucy \(cyberpunk\):1.2),(eyes looking at viewer:1.4), black tight suit, black leotard, black pants, (detached sleeves), pouch, (white hair), (asymmetric hair), (short hair), (multicolor hair), (bang:1.2), (beautiful detailed hair) , red eyeliners, short shorts<lora:lucyCyberpunk_35Epochs:0.8>

          negative prompts

          paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale))

          ก็จะได้ผลลัพธ์ประมาณว่า มัน Generate หน้าผมไปใส่ชุด ใส่ทรงผมของ Lucy รวมแล้วกลายเป็นสิ่งมีชีวิตที่น่ากลัวสุดๆ ไปเลย 555 (ขออนุญาตไม่เอารูปเต็มมาใส่นะ รับไม่ได้ 555)

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 265

          ดังนั้นเราจะพบว่ามัน Generate แบบตรงไปตรงมาไม่ได้!! เพราะมันจะเอา LoRA 2 อันปนกัน แถมตัวละครก็ยังปนกันด้วย

          สมมติว่าผมพยายามจะใช้ ControlNet มาช่วย เพื่อให้เกิดการแยกเป็น 2 คน โดยใช้รูปต้นแบบประมาณนี้ (เอามาจาก https://www.pexels.com/photo/brunette-woman-posing-with-man-in-suit-15149359/) (ถ้าไม่มีรูปต้นแบบ คุณ Gen รูป AI ตัวละครทั่วไปมาเป็นต้นแบบอีกทีก็ได้)

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 266

          จากนั้นเอามาใช้ ControlNet Open Pose (ผมอัปเดท ControlNet เป็น V1.1 แล้วนะ ใครมีอันเดิมอย่าลืมอัปเดทและโหลด Model มาใหม่ด้วยล่ะ เจ๋งขึ้นเยอะเลย)

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 267

          ผลที่ได้จะยิ่งแย่กว่าเดิมอีก! เพราะแม้ว่าจะมี 2 คนออกมาแล้วจริงๆ แต่มันกลายเป็นว่าทั้ง 2 คนมีหน้าคล้ายๆ ผมทั้งคู่ แล้วรายละเอียดของชุดทุกอย่างก็ปนกันมั่วซั่วไปหมด ไม่ใช่แบบที่ผมสั่งเลย

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 268

          วิธีที่ถูกต้องในการ Generate หลายตัวละคร

          แนวทางแก้ไขก็คือ ดราจะต้องใช้ Extension มาช่วย ซึ่งผมแนะนำว่าควรจะโหลดมาเพิ่ม 2 ตัว เพื่อให้ได้ผลลัพธ์ดั่งใจมากที่สุด

          ก็ให้เข้าไปโหลด Extension ตามปกติใน Tab Extension -> Install from URL แล้ว Install ให้เรียบร้อย จะเป็นดังนี้

          จากนั้นให้เราไป Enable Extension ตามนี้

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 269

          เราจะพบว่าพอกด Visualize แล้ว ตรง Region จะถูกแบ่งเป็น 3 ก้อน (จาก Division 3 ค่าที่คั่นด้วย comma ) ซึ่งเกิดจากการตั้งค่าไล่จากซ้ายไปขวา คือ

          1. ก้อนเต็มพื้นที่ (ขนาด 1:1 แปลว่า 1 เท่าของพื้นที่รวม ) โดยสีขาวจางมาก (เพราะน้ำหนัก Weights 0.2) [position 0:0]
          2. ก้อนฝั่งซ้าย (ขนาด 1:2 แปลว่า 1/2 ของพื้นที่รวม) โดยสีขาวค่อนข้างเยอะ (เพราะน้ำหนัก Weights 0.8) [position 0:0]
          3. ก้อนฝั่งขวา (ขนาด 1:2 แปลว่า 1/2 ของพื้นที่รวม) โดยสีขาวค่อนข้างเยอะ (เพราะน้ำหนัก Weights 0.8) [position 0:1]

          ซึ่งแปลว่าเราจะต้องแบ่ง Prompt เป็น 3 ส่วน โดยคั่นแต่ละส่วนด้วยคำว่า ANDละอย่าลืมใส่คำว่า (1man+1girl) เข้าไปทุกส่วนด้วย (ถ้าเป็นผู้หญิง 2 คนก็ 2girls)

          • โดยที่ส่วนแรกสำหรับภาพรวมทั้งหมด
          • ส่วนสองสำหรับภาพฝั่งซ้าย และ
          • ส่วนสามสำหรับภาพฝั่งขวา

          ดังนั้นผมจะแก้ Prompt เป็นดังนี้ แล้วจะลอง Generate ใหม่โดยยังไม่ใช้ ControlNet

          (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (Beautiful beach and blue sky), (1man+1girl),
          
          AND (1man+1girl),(best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (SiraEkabut Man with red brown hair), (smile), (slim body), (white collar shirt:1.3) <lora:SiraEkabutV4-crop768-512-128-50-clip1-chillout-000003:0.71>,
          
          AND (1man+1girl),(best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2),  (lucy \(cyberpunk\):1.2),(eyes looking at viewer:1.4), black tight suit, black leotard, black pants, (detached sleeves), pouch, (white hair), (asymmetric hair), (short hair), (multicolor hair), (bang:1.2), (beautiful detailed hair) , red eyeliners, short shorts<lora:lucyCyberpunk_35Epochs:0.8>

          ผลลัพธ์จะออกมาได้หลอนสุดๆ คือมีการแบ่งครึ่งพื้นที่แล้ว Generate แยกกัน ฝั่งซ้ายจะเป็นผมมากกว่า ฝั่งขวาจะดูเป็น Lucy มากกว่า ซึ่งแย่สุดๆ เพราะบางทีมันกลายเป็นมนุษย์ครึ่งซีกไปเลย 555

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 270

          ทีนี้ผมลองเกิดใช้ ControlNet Openpose เหมือนเดิม เพื่อบังคับแยกเป็น 2 คน

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 271

          ผลที่ได้ก็ยังหลอนอยู่ดี! เพราะว่าแม้จะ Gen แยกกันครั้งซีกแล้ว แยก 2 คนแล้ว แต่เหมือนว่า Effect ของ LoRA 2 ตัวมันยังปนๆ กันอยู่นั่นเอง ทำให้หน้าผมไปอยู่บนหน้า Lucy ด้วย ทุเรศมากๆๆๆ

          ทำยังไงให้ LoRA ไม่ปนกัน?

          วิธีที่จะทำให้ LoRA มันส่งผลแยกกัน เราจะต้องเปิดใช้ Extension อีกตัวด้วย นั่นคือ Composable LoRA นั่นเอง โดยติ๊กตามนี้ แล้วลอง Generate ใหม่

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 272

          ผลที่ได้ดีกว่าเดิมมากมาย คราวนี้ตัวละครแยกกันอย่างชัดเจนแล้ว

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 273

          หมายเหตุ : แต่ถ้าเราไม่เปิด controlnet (หรือมันเอ๋อไป) ผลก็อาจออกมาแปลกๆ แบบนี้ได้ (คือ Gen แยกกัน และ LoRA ก็แยกกัน เหมือนภาพตัดแปะห่วยๆ)

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 274

          จัดการ Background ที่ผิด

          ซึ่งผมต้องจัดการเรื่อง Background ที่มันยังไม่ใช่ชายหาดอย่างที่ต้องการ แต่กลับเป็น background อะไรไม่รู้ เหมือน 2 ฝั่งมาจากคนละโลกกัน

          ผมจะลองใส่คำว่า Beautiful Beach เข้าไปทั้ง part ซ้ายขวาด้วย ดูว่าจะดีขึ้นหรือไม่

          (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (Beautiful beach and blue sky), (1man+1girl),
          
          AND (1man+1girl),(best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (SiraEkabut Man with red brown hair), (smile), (slim body), (white collar shirt:1.3) , standing on Beautiful beach and blue sky <lora:SiraEkabutV4-crop768-512-128-50-clip1-chillout-000003:0.71>,
          
          AND (1man+1girl),(best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2),  (lucy \(cyberpunk\):1.2), (eyes looking at viewer:1.4), black tight suit, black leotard, black pants, (detached sleeves), pouch, (white hair), (asymmetric hair), (short hair), (multicolor hair), (bang:1.2), (beautiful detailed hair) , red eyeliners, short shorts,  standing on Beautiful beach and blue sky <lora:lucyCyberpunk_35Epochs:0.8>

          ผลออกมาเป็นทะเลแล้ว เย้!

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 275

          หลังจากนั้น เราสามารถลอง Gen หลายๆ รอบเพื่อให้ได้ภาพที่ใกล้เคียงกับที่ต้องการมากที่สุด รวมถึงปรัย Prompt เพื่อให้ได้สิ่งที่ต้องการมากขึ้นได้

          แล้วสามารถลองมา Hires-Fix กัน (เพราะ ControlNet 1.1 รองรับ Hires-Fix ได้ดีขึ้นแล้ว)

          โดย ผมปรับ prompt เป็นแบบนี้ คือเจาะจงหน้า lucy ให้สวยขึ้นด้วย (เพื่อรับรับการ Hires-fix) โดยใส่คำเพิ่ม เช่น beautiful young girl, (ulzzang-6500:0.5)

          (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (Beautiful beach and blue sky), (1man+1girl),
          
          AND (1man+1girl),(best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (SiraEkabut Man with red brown hair), (smile), (slim body), (white collar shirt:1.3) , standing on Beautiful beach and blue sky <lora:SiraEkabutV4-crop768-512-128-50-clip1-chillout-000003:0.71>,
          
          AND (1man+1girl),(best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2),  (lucy \(cyberpunk\):1.2), beautiful young girl, (ulzzang-6500:0.5), little smile, (eyes looking at viewer:1.4), black tight suit, black leotard, black pants, (detached sleeves), pouch, (white hair), (asymmetric hair), (short hair), (multicolor hair), (bang:1.2), (beautiful detailed hair) , red eyeliners, short shorts, ( making a fist:1.5), standing on Beautiful beach and blue sky <lora:lucyCyberpunk_35Epochs:0.8>

          ก่อน Hi-res fix

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 276

          หลัง Hi-res fix

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 277

          ผลที่ได้ค่อนข้าง ok แค่มีมือเพี้ยนนิดหน่อย ดังนั้นเราสามารถ Inpaint แก้รายละเอียดได้อีกถ้าต้องการครับ

          ผลลัพธ์หลังจาก Inpaint

          หลังจาก Inpaint แก้จุดต่างๆ ที่อาจจะผิดพลาดไป (ใครถนัด Photoshop ก็เชิญได้เลย) ในที่สุด ผมก็สามารถยืนคู่กับ Lucy ได้แล้วล่ะ 555

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 278

          ถ้าไม่ใช้ Control Net ได้หรือไม่?

          หลายคนอาจจะสงสัยว่า แล้วถ้าไม่ใช้ ControlNet จะบังคับให้ออกมาเป็น 2 คนได้หรือไม่? อันนี้ก็ต้องบอกว่าได้แน่นอนครับ เพียงแต่ว่า ผมแนะนำว่าควรจะทำรูปเป็นแนวนอน “จะมีโอกาส” ออกมา 2 คนง่ายกว่า (แปลว่ามันก็อาจยังออกมาเป็นมนุษย์ครึ่งซีกได้อีก) และควรปรับ Prompt นิดหน่อย ชัดๆว่าจะให้ยืนคู่กันด้วย เช่น แบบนี้

          (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (Beautiful beach and blue sky), (1man+1girl standing next to each other:1.4),
          
          AND (1man+1girl standing next to each other:1.4),(best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (portrait shot:1.4), (SiraEkabut Man with red brown hair), (smile), (slim body), (white collar shirt:1.3) , standing on Beautiful beach and blue sky <lora:SiraEkabutV4-crop768-512-128-50-clip1-chillout-000003:0.71>,
          
          AND (1man+1girl standing next to each other:1.4),(best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2),  (portrait shot:1.4), (lucy \(cyberpunk\):1.2), beautiful young girl, (ulzzang-6500:0.5), little smile, (eyes looking at viewer:1.4), black tight suit, black leotard, black pants, (detached sleeves), pouch, (white hair), (asymmetric hair), (short hair), (multicolor hair), (bang:1.2), (beautiful detailed hair) , red eyeliners, short shorts, ( making a fist:1.5), standing on Beautiful beach and blue sky <lora:lucyCyberpunk_35Epochs:0.8>

          ซึ่งผมจะได้รูปก่อน Hires-fix แบบนี้ ซึ่งดู ok เลย (ต้องไป inpaint อีกหน่อยนะ)

          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 279
          สอนทำรูปตัวเองคู่กับสาว ใน Stable Diffusion [Part6] 280

          อย่างไรก็ตาม ผลลัพธ์จะออกมาดีหรือแย่ ถ้าไม่ใช้ ControlNet ช่วย หลายๆ เรื่องจะขึ้นอยู่กับดวงพอสมควร ดังนั้นถ้าใช้ ControlNet ได้ก็จะดีกว่าครับ (ถ้าไม่มีรูปต้นแบบ คุณ Gen รูป AI ตัวละครทั่วไปมาเป็นต้นแบบก็ได้)

          ตอนต่อไป

          ในตอนต่อไปผมจะมาพูดถึงแนวทางในการที่เราจะผสม Model หลัก หรือ CheckPoint ขึ้นมาเองครับ รับรองว่าสนุกแน่นอน ติดตามได้เลย

        • สอน Train Model ตัวเองใน Stable Diffusion [Part5]

          สอน Train Model ตัวเองใน Stable Diffusion [Part5]

          ในตอนที่แล้วเราได้เรียนรู้วิธีการใช้ ControlNet ในการควบคุมท่าทางของแบบไปแล้ว ในบทความนี้เราจะมาเรียนรู้เรื่องที่หลายคนกำลังรอคอยอยู่ นั่นคือวิธีการ Train Model ของเราขึ้นมาใช้เองซักทีครับ

          ซึ่งตัว Model ที่ผมจะสอน Train ในบทความนี้ จะเป็น Model ประเภทที่เรียกว่า LoRA ซึ่งก็คือ Model เสริมที่สามารถนำไปใช้ควบคู่กับ Model หลักต่างๆ เพื่อให้สามารถสร้างตัวละคร ชุด ฉาก หรือแม้แต่สไตล์แบบที่เราต้องการได้

          แต่ขอบอกไว้ก่อนว่าสิ่งที่ผมกำลังสอนนี้ อาจไม่ใช่วิธี Train LoRA ที่ดีที่สุดที่เป็นไปได้ (เพราะมันขึ้นกับหลายปัจจัยมากๆ ต้องทดสอบเยอะมากจึงจะฟันธงแน่ชัดได้) แต่ผมลองดล้วมันดีในเคสของผมเท่านั้นเองครับ ดังนั้นถ้าใครมีวิธีที่ดีกว่านี้ สามารถบอกผมได้นะ จะได้แลกเปลี่ยนความรู้กันครับ

          และเพื่อไม่ให้มีปัญหากับประเด็นเรื่องลิขสิทธิ์ต่างๆ ผมจะแสดงตัวอย่างการ Train ภาพของตัวผมเองแล้วกันนะครับ

          รวมบทความ Stable Diffusion

            ซึ่งเราสามารถ Train Model ขึ้นมาได้ด้วยหลายวิธี ในบทความนี้ผมจะสอนวิธีแบบใช้ GPU เครื่องคอมพิวเตอร์ตัวเองซึ่ง เราจะต้องผ่านขั้นตอนหลักๆ 4 ขั้นตอน ดังนี้

            Step1 : ติดตั้งเครื่องมือ kohya_ss

            เริ่มจะเริ่มจากการลงโปรแกรมเพิ่มก่อน ซึ่งในที่นี้ผมจะใช้เครื่องมือที่ชื่อว่า kohya_ss มาช่วยครับ ซึ่งเราจะต้องลงโปรแกรมให้เรียบร้อยก่อน โดยทำตามนี้ครับ

            • ให้ลงโปรแกรมที่จำเป็นเหล่านี้ก่อน
              • Python 3.10 (ถ้าลง SD มาแล้วก็ต้องมีแล้วล่ะ)
              • Git (ถ้าลง SD มาแล้วก็ต้องมีแล้วล่ะ)
              • Install Visual Studio 2015, 2017, 2019, and 2022 redistributable (อันนี้ถ้าใครยังไม่มีต้องลงเพิ่มนะ)
            • แล้วเข้าไปใน Folder กฃที่จะใส่ Kohya_SS เข้าไป
            • แล้วเข้า CMD แล้วพิมพ์ว่า
            git clone https://github.com/bmaltais/kohya_ss.git
            cd kohya_ss
            .\setup.bat

            พอกด Enter มันจะ copy ไฟล์มาให้เรา แล้วให้กด Enter อีกทีเพื่อ setup (ถ้ามันไม่รัน setup ให้ ค่อยเข้าไปรันไฟล์ setup ใน folder เอง)

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 281

            แล้วมันจะโหลดไฟล์เพียบเลย

            แต่ถ้ารัน setup แล้วมันถาม เรื่อง previous version
            ก็ตอบประมาณนี้ก็ได้

            • No (ไม่ Uninstall)
            • Torch ถ้าการ์จกแรงเลือก 2 ได้เลย แต่ถ้าไม่มั่นใจเลือก 1.1

            พอโหลดเสร็จมันจะเริ่มถามคำถาม ให้ตอบตามนี้ทีละขั้นตอนได้เลย

            • This Machine
            • No
            • No
            • No
            • All
            • fp16 (ถ้าใครการ์ดจอแรงๆ อาจตอบ bp16 ได้)

            เท่านี้การลงโปรแกรมก็เสร็จสิ้นแล้ว

            Step 2 : เตรียม Data Set ภาพตัวอย่าง

            ขั้นตอนนี้ เป็นขั้นตอนที่ดูเหมือนจะน่าเบื่อแต่สำคัญมากๆ เลย เพราะมันคือขั้นตอนที่เราจะต้องคัดเลือกตัวอย่างมาสอนเจ้า AI ให้สามารถ Generate รูปที่เราต้องการได้ ซึ่งเราจะต้องคัดเลือกรูปตัวอย่างให้เหมาะสมจึงจะได้ผลลัพธ์ที่ดี

            สมมติว่าเราอยากจะเทรนหน้าตัวเองให้ AI รู้จัก ซึ่งแนวคิดคือ AI จะต้องเห็นรูปตัวอย่างของเราจำนวนที่มากพอ และหลากหลายพอ ที่จะสามารถวาดรูปเราขึ้นมาใหม่ได้ ในลักษณะท่าทางหรือชุดดที่ต่างไปจากรูปทีเทรนได้ด้วย

            ซึ่งจำนวนรูปที่เหมาะสม

            • ผมแนะนำว่าให้เราเตรียมรูปหน้าของเราประมาณ 15-40 รูป
            • แต่จำนวนไม่สำคัญเท่าคุณภาพ ภาพที่ได้มาต้องมีความชัดเจน AI ดูแล้วไม่สับสน
            • และที่สำคัญ ยิ่งมีรูปเยอะ เรายิ่งต้องบรรยายเยอะ (ซึ่งคือส่วนที่น่าจะจุกจิกที่สุดแล้ว)

            ส่วนเรื่องความหลากหลาย ยิ่งหลากหลาย ยิ่งเพิ่มความยืดหยุ่น แต่ก็จะมีภาระในการที่ต้องบรรยายรูปซับซ้อนขึ้นไปด้วย หลักการคือ อย่าพยายามมีอะไรที่เราไม่ต้องการอยู่ในรูปซ้ำๆ เยอะๆ เดี๋ยวมันจะจำสิ่งนั้น แต่ถ้าเลี่ยงไม่ได้ ต้องบรรยายสิ่งนั้นด้วย

            ถ้าเราอยากให้ LoRA เรา Flexible มากหน่อย ก็ควรมีดังนี้

            • ผมแนะนำว่าควรมีหลายมุมมอง ทั้งหน้าตรง เฉียง ข้าง มุมเงย กด ได้ยิ่งดี
            • ถ้าอยากให้เปลี่ยนทรงผมได้ ในรูปก็ควรจะมีเราทำผมมากกว่า 1 แบบ
            • ถ้าอยากให้เปลี่ยนชุดได้ก็ควรจะมีชุดที่หลากหลายด้วย
            • ลักษณะของแสง ก็ควรจะมีหลากหลาย ทั้งไฟธรรมชาติ ไฟในห้อง ไฟขาว ไฟส้ม

            ซึ่งถ้าสามารถทำตามที่แนะนำได้ ก็จะได้ผลลัพธ์ที่ดีกว่า Flexible กว่า แต่ถ้าทำไม่ได้ก็ไม่เป็นไร มันก็เทรนได้เช่นกัน อย่าเพิ่งซีเรียสมาก ลองหารูปมาแล้วเทรนดูก่อนซักตั้ง ดีกว่ามัวแต่เตรียมไฟล์แล้วไม่เทรนซักที 555

            ตัวอย่างเช่น ของผมเองผมเตรียมไว้ดังนี้ (ของผม 35 รูป)

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 282

            ถ้าเราจะเทรนแค่หน้าของเราเป็นหลัก ไม่ได้สนใจเรื่องชุดหรือเครื่องแต่งกาย เราสามารถทำการ Crop ภาพให้เน้นแค่หน้าเราได้ครับ ซึ่งจะลดโอกาสที่ AI จะจำชุดหรือสิ่งแวดล้อมของรูปลงไปได้ดีพอสมควร

            ซึ่งเครื่องมือหนึ่งที่สะดวกมากในการ Crop รูป คือเว็บที่ชื่อว่า https://www.birme.net/

            ให้เราเข้าไปในเว็บแล้ว Browse รูปลงไป จากนั้นทำการ Resize รูป แล้วเลือกบริเวณ Crop เพื่อเอาเฉพาะส่วนหน้าได้ตามต้องการ ซึ่งเค้านิยมทำกันที่ 512×512 ไม่ก็ 768×768

            ซึ่งในที่นี้ผมเลือก 768×768 ไว้เพราะละเอียดดี (ถ้าเทรนชุดก็ไม่ต้อง Crop แบบจตุรัสก็ได้ เพราะจะเห็นชุดไม่ครบ)

            จากนั้น save เป็นไฟล์ zip ออกมา

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 283

            จากนั้นให้เตรียม Folder ไว้ 3 อันคือ (ชื่อจะตั้งยังไงก็ได้ไม่ซีเรียส แต่ส่วนใหญ่เค้าจะตั้งชื่อประมาณนี้กัน)

            • Image
            • Model
            • Log

            จากนั้นใน Folder Image ให้เราสร้าง Folder ย่อยขึ้นมา (สามารถมี folder ย่อยอันเดียว หรือ หลาย folder ย่อยก็ได้ แต่ในที่นี้เราจะทำแค่ folder ย่อยเดียว) โดยให้ตั้งชื่ออยู่ในรูปแบบนี้ คือ

            ตัวเลข_ข้อความ

            โดยตัวเลข คือจำนวน Repeats หรือการทำซ้ำที่เราจะ train ในแต่ละรูป ซึ่งเป้าหมายหลักคือ เราต้องเทรนทั้งหมดอย่างน้อย 1500 steps ขึ้นไป ยกตัวอย่างเช่น

            • สมมติว่าถ้าเรามีทั้งหมด 10 รูป เราก็ต้องเทรนอย่างน้อย 1500/10 = 150 Repeats
            • สมมติว่าถ้าเรามีทั้งหมด 20 รูป เราก็ต้องเทรนอย่างน้อย 1500/20 = 75 Repeats
            • สมมติว่าถ้าเรามีทั้งหมด 30 รูป เราก็ต้องเทรนอย่างน้อย 1500/30 = 50 Repeats

            มีคนแนะนำว่า ควรจะตั้งตัวเลข repeats ไม่ต่ำกว่า 100 แต่เท่าที่ผมลอง หากเราทำการ train หลาย epoch (หลายรอบ) ผมตั้งที่ 50 ก็ยังออกมาเหมือนอยู่ครับ

            ดังนั้นในเคสนี้ ผมจะตั้งชื่อเป็น 50_SiraEkabut แล้วใส่รูปที่ Crop แล้วเข้าไปแบบนี้ (ผมมี 35 รูป)

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 284

            Step 3 : เตรียม Text Description ให้แต่ละรูป

            หลักการคือเราจะต้องสร้าง text file นามสกุล .txt ที่ชื่อตรงกับรูปแต่ละรูป แล้วบรรยายสิ่งที่อยู่ในรูปนั้น เพื่อให้ AI เข้าใจว่ารูปนั้นๆ คือรูปอะไรกันแน่

            ซึ่งแนวทางการใส่ Description มีหลายวิธี คือ

            • สร้าง Text ไฟล์ขึ้นมาเอง แล้วพิมพ์บรรยายเองเลย (ถึกจัดๆ ไม่ค่อยแนะนำ)
            • ใช้เครื่องมือช่วยใส่คำบรรยายอัตโนมัติ แล้วค่อยพิมพ์แก้เอา ซึ่งทำได้หลายวิธีมากๆ (แบบนี้ผมว่าดี)
              • ใช้เครื่องมือ BLIP Captioning ใน Kohya_SS Gui (ค่อนข้างดี)
              • ใช้เครื่องมือ WD14 Tagger ใน Kohya_SS Gui
              • ใช้เครื่องมือ Clip interrogator ใน Automatic1111
              • ใช้เครื่องมือ WD14 Tagger Extension ใน Automatic1111
              • ใช้เครื่องมือ Clip interrogator2 ใน Hugging face (ค่อนข้างดี)
              • ใช้ /describe ใน MidJourney (ค่อนข้างดี)

            Tips : ใครสนใจว่า Clip interrogator2 vs Describe ต่างกันขนาดไหน ลองดูคลิปนี้

            ในที่นี้ผมจะลองเปรียบเทียบบางเครื่องมือให้ดูด้วยแล้วกันครับ โดยผมจะลองเอารูปนี้เป็นตัวอย่าง

            ตัวอย่างเครื่องมือ Captioning

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 285

            BLIP Captioning

            ให้เรารันไฟล์ gui.bat ใน folder ที่เราลง kohya_ss เอาไว้ จากนั้นกด Ctrl+Click ที่ URL

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 286

            อันแรกลองใช้ BLIP Captioning ใน Utilities ก่อน ให้ Browse ไปที่ folder ที่ใส่รูปเอาไว้ได้เลย แล้วปรับ Min Max Length ของ Description ตามความเหมาะสม แล้วกด Caption Images

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 287

            แล้วเราจะได้ Text Description ออกมาอัตโนมัติเลย ในรูปแบบประโยคภาษามนุษย์ (ถ้ามันบรรยายน้อยไปก็เพิ่ม Min Length ให้เยอะขึ้นได้)

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 288

            ถ้าเพิ่ม Min Length เป็น 20 ก็จะได้คำบรรยายที่เยอะขึ้น (แต่บางทีก็ซ้ำไปซ้ำมา)

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 289

            WD14 Captioning

            ใช้ Kohya_SS Gui เหมือนกัน จากนั้นไปที่ Utilities -> WD14 Captioning

            (อย่าลืมย้าย text จาก BLIP ไปที่อื่นก่อน เดี๋ยวทับกัน)

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 290
            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 291

            ผลลัพธ์จะออกมาเป็น Tag หรือ Keyword ในรูปนั้นๆ แทนที่จะเป็นประโยคแบบ BLIP ซึ่งก็ดีทั้งคู่นะ

            Clip Interrogator 2 Hugging Face

            ให้ไปที่ https://huggingface.co/spaces/fffiloni/CLIP-Interrogator-2 แล้ว Upload รูปเข้าไป แล้ว submit

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 292

            จะเห็นว่า Model นี้ก็บรรยายได้ดีเลย แถมเดาด้วยว่าผมอายุเท่าไหร่ (แต่ผิดนิดๆ เพราะจริงๆ ปีนี้ผมอายุ 40 แล้วนะ)

            MidJourney Describe

            ใครมี Midjourney ก็สามารถใช้คำสั่ง /describe แล้ว Upload รูปที่ต้องการเข้าไปได้ครับ

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 293

            ของ MJ นี่น่าจะเหมาะกับรูปที่ไม่ใช่รูปจริงมากกว่าครับผมว่า

            แก้ Description ให้เหมาะสม

            ไม่ว่าเราจะใช้เครื่องมือไหนในการ Gen Text เบื้องต้นออกมา สุดท้ายเราจะต้องปรับแต่ให้เหมาะสมด้วย ซึ่งเป็นขั้นตอนที่สำคัญสุดๆ และต้องใส่ใจในรายละเอียดมากที่สุด

            เช่น เดิม Caption ที่ได้จาก BLIP Captioning เป็นแบบนี้

            a man sitting on a bench next to a waterfall in a forest area with a wooden bench and a waterfall behind him

            อย่างน้อยที่สุด เราจะต้องใส่ Keyword ที่จะใช้ Trigger หน้าตาของเราเข้าไป ซึ่งผมแนะนำว่า “ให้เป็นเป็นคำเดียว และเป็นคำที่ไม่ซ้ำกับศัพท์ที่มีอยู่แล้ว” (ไม่งั้น AI จะงง)

            เช่น ผมจะพยายามใช้ Keyword “SiraEkabut” เพื่อให้เป็นตัวแทนของหน้าตาของผม โดยใส่เข้าไปใน Description แบบนี้

            SiraEkabut man sitting on a bench next to a waterfall in a forest area with a wooden bench and a waterfall behind him

            หลักการทำงานคือ “AI จะพยายามเชื่อมโยงทุกสิ่งที่อยู่ในรูปที่เทรน เข้ากับคำบรรยายที่เราใส่”

            ซึ่งถ้าผมใส่คำบรรยายไปแค่นี้ มันก็พยายามจะเชื่อมโยงพวกเสื้อสีม่วง สร้อยข้อมือ กางเกงยีนส์สีเทาดำเข้าไปกับคำบรรยายด้วย

            ทีนี้เนื่องจากว่ามันน่าจะรู้จักคำบรรยายอื่นๆ หมดแล้ว เช่น man sitting on a bench next to a waterfall in a forest area with a wooden bench and a waterfall behind him

            ดังนั้นพวกเสื้อสีม่วง สร้อยข้อมือ กางเกงยีนส์สีเทาดำ “ก็อาจจะถูกเชื่อมโยง” เข้ากับ Keyword ที่เราใส่ว่า SiraEkabut ไปด้วย (ซึ่งไม่ใช่สิ่งที่ผมต้องการ)

            ยิ่งถ้าเรามีรูปใส่ชุดเดิมเยอะๆ มันจะยิ่งจำฝังใจเลย

            • ถ้าผมอยากให้ SiraEkabut หมายถึงหน้าตาและทรงผมของผมเท่านั้น ไม่ต้องรวมเครื่องแต่งกาย ผมก็ควรใส่คำบรรยายแบบนี้
              • SiraEkabut man wearing purple morhom shirt and dark grey jeans with black neck strap and red bracelet sitting on a bench next to a waterfall in a forest area with a wooden bench and a waterfall behind him
              • การใส่คำบรรยายชุดเข้าไป เรื่องชุดมันจะได้ผูกกับ description เกี่ยวกับชุด จะได้ไม่ผูกเข้ากับ SiraEkabut
            • แต่ถ้าอยากให้ SiraEkabut สามารถเปลี่ยนสีผมได้ ผมก็ต้องบรรยายเรื่องเกี่ยวกับสีผมเข้าไปด้วย (มันจะได้ไม่ผูกสีผมเข้ากับ SiraEkabut) ดังนี้
              • SiraEkabut man with black hair wearing purple morhom shirt and dark grey jeans with black neck strap and red bracelet sitting on a bench next to a waterfall in a forest area with a wooden bench and a waterfall behind him

            ดังนั้นหลักการโดยสรุป คือ หลังจากใส่ Keyword พิเศษเข้าไปแล้ว ให้ใส่คำบรรยายทุกอย่างในรูปที่ไม่ต้องการผูกเข้ากับ Keyword เข้าไปให้หมด ยิ่งใส่ได้ละเอียดยิ่งดี

            หมายเหตุ : ถ้าเราไม่มี text file ที่ใส่คำบรรยายรูป มันจะเอาชื่อ Folder มาใช้เป็น Description แทน (ถ้าใส่ text file คำบรรยายมันจะไม่สนใจชื่อ Folder)

            จากนั้นให้แก้ไขคำบรรยายให้ครบทุกรูป ก็เป็นอันเสร็จ!

            Step 4 : เริ่มการ Train LoRA

            และแล้วก็มาถึง Step ที่เราจะเริ่มเทรน LoRA กันจริงๆ แล้วครับ ขั้นตอนคือให้เข้า Gui.bat ใน Kohya_ss เหมือนเดิม แล้วเข้าไป Tab Dreambooth LoRA

            จากนั้นผมแนะนำให้เลือก Pretrained Model ที่จะใช้ Train LoRA ของเราอีกที

            ซึ่งแนวทางคือ ยิ่งเราเลือก Model ที่เราตั้งใจจะใช้ LoRA นั้น จะยิ่งได้ผลลัพธ์ที่เหมือนเป๊ะกว่า แต่ความยืดหยุ่นในการใช้งาน ก็อาจจะน้อยกว่าการ Train บน Model ปกติอย่าง SD1.5 ที่อาจเอาไปใช้กับ Model ได้หลากหลายกว่า แต่ไม่ค่อยสวยซักอัน 555 (อันนี้ต้องเลือกเอาว่าจะเอาสวย หรือเอายืดหยุ่น)

            เช่น ผมคิดว่าหลักๆ ผมจะใช้ ChillOutMix ในการ Gen ภาพ ดังนั้นผมก็จะ Train บน ChillOutMix ละครับ

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 294

            จากนั้นไปที่ Folder แล้วเลือก 3 Folder ที่เราเตรียมไว้ คือ Image (ไม่ใช่ Folder ย่อย) , Model, Log ดังนี้

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 295

            จากนั้นเราจะไปตั้งค่าใน Training Parameters กัน

            ปรับค่าประมาณนี้

            • epoch คือ จำนวนรอบในการ Train ซึ่งในที่นี้ผมจะ Train 3 รอบ แต่ละรอบจะค่อยๆ เหมือนขึ้นเรื่อยๆ
              • แต่ในรอบหลังๆ LoRA จะเหมือนต้นฉบับเกินไปจนอาจไม่ยืดหยุ่น คือเปลี่ยนเสื้อผ้า ทรงผมอาจจะลำบาก
              • ดังนั้นเราจะให้มัน Save ไฟล์ LoRA ทุกๆ 1 Epoch แล้วค่อยมาเลือกเอาอีกทีว่าจะเอาระดับไหนดี
            • Caption Extension ให้ใส่ว่า .txt ตามคำบรรยายที่เราทำ
            • Seed ใส่เลขอะไรก็ได้ แต่ควรใส่ Fixed เลขไว้ซักค่านึง (เช่น 1234) เพื่อเราจะได้เปรียบเทียบการ Train ได้ว่า Setting แบบไหนดีกว่ากัน
            • LR Scheduler อันนี้ผมเลือกเป็น Constant
            • LR warmup ใส่ 0
            • พวก Learning Rate ยิ่งน้อยยิ่งแม่น แต่ต้องเทรน Step เยอะขึ้น (เช่นเพิ่ม Epoch เข้าไป) ผมแนะนำว่าไม่ต้องแก้ไขก็ดีอยู่แล้ว
            • Network Rank กับ Alpha ใส่ได้ตั้งแต่ 1, 8,16, 32, 64,128 ซึ่งเลขยิ่งเยอะไฟล์ LoRA จะยิ่งใหญ่ และมักจะใส่เป็นเลขเดียวกัน
              • โดยเลขเยอะ (เท่าที่ผมเข้าใจ) จะเหมาะกับการ Train ที่มีรายละเอียดเยอะ เช่น หน้าตาสมจริงมากขึ้น เลขน้อยๆ ค่อนข้างเหมาะกับ Train Style มากกว่า
              • ในที่นี้ผมเลยเลือก 128 ทั้งคู่
            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 296

            จากนั้นใน Advanced Configuration จะมีอีกอันนึงที่น่าสนใจ คือ CLIP Skip ซึ่งเค้าจะใส่กันว่าจะเป็น 1 ไม่ก็ 2 (เท่าที่อ่านมา 1 จะเหมาะกับภาพสมจริง 2 จะเหมาะกับภาพการ์ตูนมากกว่า แต่เดี๋ยวผมลองให้ดูว่าผลออกมาเป็นไง) ในที่นี้ผมเลือก 1

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 297

            ป.ล. พอตั้งค่าทุกอย่างหมดแล้ว จะ Save Config เอาไว้เพื่อใช้ Train อย่างอื่นภายหลังก็ได้

            แล้วก็กด ปุ่มส้มๆ เพื่อเริ่ม Train Model ได้เลย

            แล้วหน้าจอดำๆ ก็จะวิ่งไปเรื่อยๆ แต่อย่างน้อยมันต้องเจอรูปเรา แล้วบอกจำนวน Steps ทั้งหมด (ในที่นี้ของผมคือ 1750 steps x 3 epochs = รวม 5250 steps)

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 298
            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 299

            แล้วเราก็รอมันไปจนกว่าจะ Train เสร็จ แล้วเราจะได้ LoRA ออกมา 3 ไฟล์ คือ

            • SiraEkabutV1.safetensors
            • SiraEkabutV1-000001.safetensors
            • SiraEkabutV1-000002.safetensors

            ผมแนะนำให้เปลี่ยนชื่อไฟล์ SiraEkabutV1.safetensors เป็น SiraEkabutV1-000003.safetensors เพราะว่ามันคือการ Train รอบสุดท้ายนั่นเอง

            ลองเอา LoRA ที่ได้มาทดสอบ Gen รูป

            ให้ Copy ไฟล์ LoRA ที่เรา Train ได้ออกมาไว้ใน Folder stable-diffusion-webui\models\Lora ตามปกติ แล้วเราจะใช้ xyz plot ในการทดสอบดูว่า LoRA แต่ละตัวให้ผลเป็นยังไง แล้วน้ำหนัก LoRA ที่เปลี่ยนไปจะส่งผลขนาดไหน

            ในที่นี้ผมได้มีการ Train LoRA มาหลายแบบ เพื่อเอามาลองทดสอบให้เพื่อนๆ ดู โดยใช้ x/y/z plot แบบ Prompt S/R มาช่วย

            ใน Prompt ผมเขียนว่า

            (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2),(portrait shot:1.3)(SiraEkabut Man:1.3), (wearing white shirt and jeans pants:1.1),(walking in the street), <lora:SiraEkabutV4-000001:0.61>

            ผมอยากจะเปลี่ยน LoRA กับ Weight ไปเรื่อยๆ ผมเลยต้องใส่ใน x/y/z plot แบบนี้

            Prompt S/R จะค้นหาคำแรกใน Prompt แล้วแทนด้วยคำต่อไปเรื่อยๆ จนครบ

            ก็เลยต้องใส่ชื่อ LoRA ทุกอันแบบนี้ (โดยอันแรกคือ LoRA ใน Prompt)

            SiraEkabutV4-000001,SiraEkabutV4-000002,SiraEkabutV4-000003,SiraEkabutV4-crop768-512-128-50-clip1-chillout-000001,SiraEkabutV4-crop768-512-128-50-clip1-chillout-000002,SiraEkabutV4-crop768-512-128-50-clip1-chillout-000003,SiraEkabutV4-crop768-512-128-50-clip1-chillout-000004,…

            ส่วนเรื่อง Weight ผมใส่แบบนี้ (โดยอันแรกคือ Weight ใน Prompt)

            0.61,0.7,0.8,0.9,1.0

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 300

            จากนั้นก็ทำการ Gen ภาพทดสอบดูว่า LoRA ตัวไหนน่าจะ Work ที่สุด (LoRA ที่ดี ภาพไม่ควรจะเน่า หรือติดสิ่งที่ไม่ต้องการเข้ามา เช่น ชุด จนเปลี่ยนชุดไม่ได้)

            ผลการทดสอบ x/y/z plot

            ผลที่ได้เป็นแบบนี้ ซึ่งผมสรุปได้ดังนี้

            • ตัวที่ Train จาก Model ตัวเดียวกับที่ใช้ Gen รูปจะให้ผลที่ดีกว่า เช่น
              • ถ้า Gen ภาพบน ChillOutMix ตัว LoRA ที่เทรนจาก pretrained ChillOutMix จะให้ผลที่ดีที่สุด
              • ถ้า Gen บน checkpoint model อื่น เช่น dalcefo_realistic_tally_v3 ตัว LoRA ที่เทรนจาก pretrained dalcefo_realistic_tally_v3 ก็จะให้ผลดีที่สุด (แต่ตัวที่เทรนจาก Chilloutmix ก็ออกมาไม่เลว)
              • ตัวที่เทรนจาก SD 1.5 ปกติ แม้แต่เอามา Gen บน SD1.5 เลย ก็สู้ตัวที่เทรนจาก Model สวยๆ ไม่ได้
            • Clip Skip 1 กับ Clip Skip 2 ในเคสนี้ให้ผลไม่ต่างกันเท่าไหร่
            • ตัวที่ Train Epoch แรกๆ จะไม่เหมือนเท่าไหร่ ส่วน Epoch หลังๆ ภาพที่ weight สูงๆ จะเน่าเกินไป ตัวที่ผมคิดว่าพอใช้ได้ในเคสของผมคือ Epoch ที่ 2-3 เช่น ที่ลงท้ายด้วย 000002, 000003 (ทั้งนี้เพราะผม repeats ที่ 50 ตรง folder ด้วย)

            รูปนี้ Gen บน ChillOutMix ตัวที่เทรนจาก ChillOutMix จะสวยสุดอย่างชัดเจน

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 301

            ลองทดสอบ Generate รูปให้หลากหลาย

            พอเราได้ LoRA อันที่พอใจแล้ว ก็อาจเอามาลองทดสอบภาพอื่นๆ บ้าง เช่น เปลี่ยนสีชุด สีผม ลองยิ้มดู ว่ามัน work หรือไม่? เช่น ลองผมแปลกๆ ลองหันข้าง ลองใส่ชุดแปลกๆ (แบบที่เราไม่เคยเทรนมันเลย) เป็นต้น

            ซึ่งผมได้รูปออกมาประมาณนี้ ซึ่งน่าพอใจทีเดียว มันก็สามารถ Gen เราทำผมแปลกๆ ทั้งผมทอง ชุดซามูไร หรือแม้แต่เสกให้ผมเป็นนักร้องผมทองบนเวทีได้ด้วย สนุกมากเลย 5555

            ลองสังเกตดูสิ รูปพวกนี้ไม่มีในรูปที่ผมเอามาเทรนเลยแม้แต่น้อยนะ!! แต่มันก็สามารถเอาหน้าผมไปใส่ในรูปได้อย่างแนบเนียน มันเป็นอะไรที่สุดยอดมากเลย!!

            ป.ล. หลายรูปที่ Gen ออกมาใช้ไม่ได้ก็มี อันนี้คือเลือกที่ ok มาให้ดู แต่ไม่ได้มี inpaint ทับเลยนะ gen สด ทุกรูปครับ

            ตอนต่อไป

            ในตอนต่อไป เราจะลองเอาตัวเราเอง ไปยืนคู่กับสาวๆ น่ารักๆ บ้างแล้วคล้ายๆ แบบนี้ (รูปนี้ไม่ใช่รูปจริงนะ อย่าไปฟ้องภรรยาผม 555)

            ซึ่งมันต้องรู้เทคนิคบางอย่างจึงจะทำได้ ไม่งั้น LoRA ของตัวละครได้ตีกันมั่วเลย เพื่อนๆ ลองทำเล่นๆ ดูก่อนก็ได้ว่าออกมาเป็นไง 55

            สอน Train Model ตัวเองใน Stable Diffusion [Part5] 311

            ดังนั้น ใครอยากรู้วิธีทำให้มัน work ก็รอตอนต่อไปได้เลยครับ

            สำหรับตอนนี้ ใคร Gen ภาพตัวเองออกมาแล้วได้ผลเป็นยังไง อย่าลืมบอกกันด้วยนะที่เพจ https://www.facebook.com/AiAngelGallery/ นะครับ

          • 10 Levels การเขียนสูตร Excel ในยุคใหม่

            10 Levels การเขียนสูตร Excel ในยุคใหม่

            ในบทความก่อนหน้านี้ผมได้เขียนบทความ บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query ไป ซึ่งจะต้องเป็นคนใช้งาน Power Query ขั้นสูงจึงจะได้ใช้มัน

            คราวนี้เลยขอขยับมาเขียนบทความที่คนส่วนใหญ่สามารถอ่านได้ ซึ่งก็คือ การเขียนสูตร Excel นั่นเอง

            มาดูกันเล่นๆ ว่าการเขียนสูตร Excel ของคุณอยู่ในระดับไหน อิอิ

            LV1: การอ้างอิง Cell และ Operator พื้นฐาน

            • บวก ลบ คูณ หาร ทั่วไป :
              =(A1+B1)*C1
            • การใช้ $ ตรึงตำแหน่ง Cell แบบ Absolute Reference :
              = (A1+B1)*$C$1
            • การใช้ $ ตรึงตำแหน่ง Cell แบบ Mixed Reference :
              = (A1+B1)*C$1
            • การแปลง Text ให้กลายเป็นเลข
              =A1*1 หรือ =–A1
            • การเชื่อมข้อความเข้าด้วยกัน
              =A1&B1
            • การอ้างอิงข้อความในสูตร
              =”คุณ “&A1
            • การเปรียบเทียบให้ได้ค่า TRUE/FALSE
              =A1>B1
            • การตรวจสอบว่า cell ที่สนใจเป็นค่าว่างหรือ Blank text หรือไม่
              =A1=””

            LV2: ฟังก์ชันพื้นฐานที่ Input ไม่ซับซ้อน : input ไม่เกิน 1 ชุด

            • การหาผลรวม
              =SUM(number1,[number2],…)
            • วันที่ปัจจุบัน:
              =TODAY()
            • นับจำนวนอักขระ
              = LEN(text)
            • หาเลขปีจากวันที่
              =YEAR(serial_number)
            • Absolute value
              =ABS(number)
            • ตรวจสอบว่าเป็นตัวเลขหรือไม่
              =ISNUMBER(value)

            LV3: ฟังก์ชันพื้นฐานที่ Input ไม่ซับซ้อน : input เกิน 1 ชุด

            • การเขียนเงื่อนไข
              =IF(logical_test,[value_if_true],[value_if_false])
            • การปัดเศษตามหลักคณิตศาสตร์
              =ROUND(number,num_digits)
            • การหาเศษเหลือของการหาร
              =MOD(number,divisor)
            • การดึงข้อความที่อยู่ด้านซ้าย
              =LEFT(text,[num_chars])
            • การดึงบางส่วนของข้อความจากตำแหน่งที่ระบุ
              =MID(text,start_num,num_chars)
            • การหาตำแหน่งของอักขระแบบไม่สนพิมพ์เล็กพิมพ์ใหญ่ =FIND(find_text,within_text,start_num)
            • การแทนที่ข้อความ
              =SUBSTITUTE(text,old_text,new_text,[instance_num])
            • การเช็คค่าแล้วถ้า error ให้เปลี่ยนเป็นอีกค่า
              =IFERROR(value,value_if_error)

            LV4: ฟังก์ชันที่รับ input ค่อนข้างซับซ้อน

            • การหาผลรวมแบบมีเงื่อนไข รายละเอียดดูคลิปนี้
              =SUMIFS(sum_range,criteria_range,criteria,…)
            • แปลงเลขเป็น Text ด้วย Custom Format ที่ระบุ
              ซึ่งคุณต้องเข้าใจเรื่อง Custom Format ก่อน เช่น
              =TEXT(value,format_text) เช่น
              =TEXT(วันที่,”yyyy-mm-dd”)
              =TEXT(เลขเบอร์โทรศัพท์,”000-000-0000″)
            • การหาวันครบกำหนด
              =WORKDAY.INTL(start_date,days,weekend,holidays)
              ซึ่ง weekend สามารถใส่วันหยุดประจำสัปดาห์แบบ Custom เช่น “0010010” ได้
              โดยที่เริ่มจาก จันทร์-อาทิตย์ 0=ไม่หยุด, 1=หยุด
              นั่นคือ “0010010” แปลว่า หยุดวันพุธกับเสาร์
            • การคำนวณมูลค่าเงินในอนาคต
              =FV(rate,nper,pmt,[pv],[type])
              เงินเข้าตัวเป็นบวก เงินออกเป็นลบ หน่วยของ rate, nper, pmt ต้องสอดคล้องกัน
            • การแปลงข้อความเป็น Cell Reference :
              =INDIRECT(ref_text,[a1]) เช่น
              =INDIRECT(“‘”&C1&”‘!$A$1:$A$50”) คือ
              อ้างอิงข้อมูลจาก $A$1:$A$50 จากชื่อชีทที่ระบุใน C1
            • การดึง Cell Reference จากพิกัดที่ระบุ
              =INDEX(array,row_num,column_num)
            • การดึง Lookup ข้อมูล รายละเอียดดูคลิปนี้
              =VLOOKUP(lookup_value,table_array,col_index_num,[range_lookup])
            • การ Lookup ด้วย XLOOKUP รายละเอียดดูคลิปนี้ =XLOOKUP(lookup_value,lookup_array,return_array,[if_not_found],[match_mode],[search_mode])
            • การเลื่อนตำแหน่ง Range
              =OFFSET(reference,rows,cols,[height],[width])

            LV5: การใช้ฟังก์ชันซ้อนกัน

            • การเขียน IF ผสม AND,OR :
              =IF(AND(A1 > B1, B1 > C1), “A มากกว่า B และ B มากกว่า C”, “อื่นๆ”)
            • การเขียน IF ซ้อน IF :
              =IF(A1 > 100, “Large”, IF(A1 > 50, “Medium”, “Small”))
            • Combining functions:
              =VLOOKUP(A1, INDIRECT(“‘”&C1&”‘!$B$1:$E$50”), 4, FALSE)
              เพื่อทำการ VLOOKUP ข้อมูลจาก $B$1:$E$50 จากชื่อชีทที่ระบุใน C1
            • การใช้ INDEX + MATCH เพื่อดึงข้อมูลแบบ Flexible
              =INDEX(A2:A5,MATCH(A8,C2:C5,0))
            10 Levels การเขียนสูตร Excel ในยุคใหม่ 312

            LV6: Array Formulas พื้นฐาน

            • ให้ผลลัพธ์ค่าเดียว
              • หาผลรวมของผลคูณทีละคู่
                =SUMPRODUCT(A1:A10, B1:B10)
              • การรวมข้อความเข้าด้วยกันด้วยตัวคั่นที่กำหนด
                =TEXTJOIN(“/”,TRUE,A1:A10)
              • การนับข้อมูลแบบไม่ซ้ำ (สำหรับ Excel version เก่า)
                =SUM(1/COUNTIFS(A1:A10, A1:A10))
            10 Levels การเขียนสูตร Excel ในยุคใหม่ 313
            • ให้ผลลัพธ์หลายค่า
              • การใช้ Array Constant ในฟังก์ชัน เพื่อหาค่า Top3
              • =LARGE(A1:A10, {1, 2, 3})
              • การเรียงข้อมูล
                =SORT(A1:A10)
              • การทำให้เหลือค่าไม่ซ้ำ
                =UNIQUE(A1:A10)
              • เข้าใจการทำงานของ Array Operation
                เช่น เมื่อ Cross Row กับ Column จะได้ออกมาเป็นตาราง
                =A3:A6*B2:D2
                10 Levels การเขียนสูตร Excel ในยุคใหม่ 314
              • การ Filter ข้อมูลตามเงื่อนไข รายละเอียดดูคลิปนี้
                =FILTER(MyData[TXID],(MyData[ยอดขาย]>300)*(MyData[วิธีการชำระเงิน]=”เงินสด”))
            10 Levels การเขียนสูตร Excel ในยุคใหม่ 315

            LV7: ใช้ LET เพื่อประกาศตัวแปร

            • พื้นที่สามเหลี่ยม เมื่อรู้ 3 ด้าน
              =LET(a, A1, b, B1, c, C1,
              s, (a + b + c) / 2,
              SQRT(s * (s – a) * (s – b) * (s – c))
              )
            • แทนที่อักขระ “-“,”/”,”|” ด้วย “_”
              =LET( inputText, A1,
              step1, SUBSTITUTE(inputText, “-“, “_”),
              step2, SUBSTITUTE(step1, “/”, “_”),
              result, SUBSTITUTE(step2, “|”, “_”)
              )
            • ดึงข้อมูลเฉพาะตัวเลข
              (แบบประกาศตัวแปรแยก step ละเอียด)
              =LET(
              inputText, A3,
              textLen, LEN(inputText),
              seq, SEQUENCE(1, textLen),
              charArray, MID(inputText, seq, 1),
              numArray, –charArray,
              isNumber, ISNUMBER(numArray),
              result, IF(isNumber, charArray, “”),
              TEXTJOIN(“”, TRUE, result)
              )
            10 Levels การเขียนสูตร Excel ในยุคใหม่ 316

            LV8: ใช้ Array Formulas ที่ค่อนข้างซับซ้อน

            • หาผลรวมของค่าที่มากกว่าในแต่ละคู่
              =SUM(IF(A1:A10 > B1:B10, A1:A10, B1:B10))
            • หาค่าเฉลี่ยถ่วงน้ำหนัก
              =SUMPRODUCT(A1:A10, B1:B10) / SUM(B1:B10)
            • VLOOKUP ข้อมูลด้านซ้าย
              =VLOOKUP(Z1, CHOOSE({1, 2}, B1:B5, A1:A5), 2, FALSE)
            • เขียนเงื่อนไข Array แบบ and ด้วย Boolean logic และสรุปค่า
              =MEDIAN(IF((A2:A7=”A”) * (B2:B7>0), B2:B7))
            • ใช้ Matrix MMULT ช่วยในการคำนวณ Array
            10 Levels การเขียนสูตร Excel ในยุคใหม่ 317

            LV9: ใช้ LAMBDA ให้เป็นประโยชน์

            LAMBDA เดี่ยวๆ ใช้สร้าง Custom Function

            • LAMBDA สามารถทำความรู้จักได้ที่นี่ หรือดูคลิปนี้
            • นับข้อมูลแบบไม่ซ้ำสำหรับ Excel Version ใหม่
              =LAMBDA(rng, COUNTA(UNIQUE(rng)))
            • ระยะห่างระหว่างจุดสองจุด
              =LAMBDA(x1, y1, x2, y2, SQRT((x2 – x1)^2 + (y2 – y1)^2))
            • พื้นที่สามเหลี่ยม เมื่อรู้ 3 ด้าน
              =LAMBDA(a, b, c, LET(s, (a + b + c) / 2, SQRT(s * (s – a) * (s – b) * (s – c))))

            LAMBDA + Helper Function เพื่อสร้างสูตรที่ซับซ้อน

            10 Levels การเขียนสูตร Excel ในยุคใหม่ 318

            LV10: เขียนสูตรที่เหมาะสมกับสถานการณ์ได้อย่างอิสระ

            ใน Level 10 นี้ จริงๆ คือการที่เราควรมีความรู้ในการเขียนสูตรได้อย่างยืดหยุ่น คือรู้ว่าเมื่อไหร่ควรใช้สูตรไหน เมื่อไหร่เหมาะกับการเขียนสูตร เมื่อไหร่เหมาะกับการใช้เครื่องมืออื่นดีกว่าเขียนสูตร

            ยกตัวอย่างเช่น ถ้าเราต้องรวบรวมตารางจาก Excel หลายๆ ไฟล์เข้าด้วยกัน การเขียนสูตรไม่มีทางสู้วิธีการใช้ Power Query หรือ เขียนโปรแกรมอย่าง VBA, Python ได้เลย

            รวมถึงงานบางเรื่องก็สามารถใช้การเขียนสูตร เพื่อช่วยหาผลลัพธ์ต่างๆที่ดีที่สุด เช่น การทำ Optimization ด้วย Excel Solver เป็นต้น

            รวมถึงควรจะหาวิธีเขียนสูตรที่คำนวณผลลัพธ์ได้รวดเร็ว ถูกต้อง เช่น หากมีการเขียนสูตรเยอะๆ พยายามอย่าใช้ Volatile Function ที่จะคำนวณตลอดเวลา (แต่ถ้าเขียนไม่กี่ช่องก็คงไม่เป็นไร)

            หรือบางทีควรจะหาวิธีเขียนสูตรที่ยืดหยุ่นขึ้นกว่าเดิม เช่น อยากจะแทนที่อักขระ “-“,”/”,”|” ด้วย “_” ในตัวอย่างข้างบน เราอาจจะใช้ REDUCE + LAMBDA ดีกว่า SUBSTITUTE หลายรอบเป็นต้น เพราะในอนาคตสามารถเพิ่มอักขระได้ง่ายกว่าการเขียน SUBSTITUTE ซ้อนไปเรื่อยๆ เป็นต้น

            =REDUCE(A3,{“-“,”/”,”|”},LAMBDA(a,v,SUBSTITUTE(a,v,”_”)))

            10 Levels การเขียนสูตร Excel ในยุคใหม่ 319

            ต่อจาก Level 10 คืออะไร?

            หลังจาก Level 10 นี้ก็ใช่ว่าการเรียนรู้ Excel จะสิ้นสุด เพราะจริงๆ แล้วโลกของ Excel นั้นช่างกว้างใหญ่ไพศาลนัก เพราะ Excel นั้นมี Function ใหม่เพิ่มมาเรื่อยๆ

            นอกจากนี้บางทีการเขียนสูตร Excel นั้นมันเต็มไปด้วยความพิสดาร เช่น การจะดึงเอาเลขจากข้อความ สามารถใช้สูตร NPV ที่เอาไว้คำนวณด้านการเงินมาประยุกต์ใช้ได้เฉยเลย ใครสนใจลองอ่านใน Link ได้ครับ

            ดังนั้นอย่าลืมลองศึกษาหาความรู้เพิ่มเติมจากผู้เชี่ยวชาญทั้งหลาย รวมถึงการทำโจทย์ปัญหาที่หลากหลาย ตัวอย่างเช่น โจทย์ที่ Excel Wizard นำมาถาม หรือศึกษาเทคนิคจาก VDO Clip Live ต่างๆ เช่น ที่เค้าใช้ตอนแข่ง Excel Speed Run

            ผมมั่นใจว่าหากลองทำตามคำแนะนำเหล่านี้ คุณจะเก่ง Excel ขึ้นอีกมากเลยครับ (ผมเองก็ยังต้องพัฒนาต่อไปเช่นกัน ฮึบๆ)

          • บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query

            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query

            ในบทความนี้ผมจะพาเพื่อนๆ ไปเรียนรู้ M Code ใน Power Query ผ่านตัวอย่าง 10 อัน โดยไล่ระดับตั้งแต่ Basic ที่สุดไปตัวอย่างที่ยากขึ้นเรื่อยๆ เหมือนเป็นการพาขึ้นบันได 10 ขั้นแรกสำหรับการเรียนรู้ MCode ครับ

            ซึ่งการใช้ MCode มันจำเป็นสำหรับคนที่เจอปัญหาการ Transform ข้อมูลที่มีความซับซ้อนกว่าปกติ เช่น แต่ละชีทแต่ละตารางข้อมูลหน้าตาไม่เหมือนกัน หรือหัวตารางอยู่ในรูปแบบที่ไม่ถูกต้อง เป็นต้น

            ใครพร้อมจะก้าวเดินไปสู่ระดับที่สูงขึ้น ตามผมมาได้เลย!

            ป.ล. บทความนี้เหมาะกับคนที่ใช้ Power Query ผ่านเครื่องมือบน User Interface เป็นอยู่แล้วนะครับ ใครที่ยังไม่แม่นพื้นฐานลองไปดู Playlist นี้ได้

            วิธีสร้าง Blank Query

            โดยให้เพื่อนๆ สร้าง Blank Query ตามรูป (จะ add เข้า Quick Access Toolbar ไว้ก็ดีนะ)

            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 320

            จากนั้นไปที่ Advanced Editor แล้วลองทำตามได้เลย

            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 321

            เอาล่ะ ตัวอย่าง 10 อันแรกจะมีอะไรบ้าง? มาดูกันครับ

            ตัวอย่าง 1 : Basic let…in…

            • ประกาศตัวแปรด้วย let แล้วเรียกผลลัพธ์ออกมาหลัง in ได้
            • ในที่นี้ประกาศตัวแปรชื่อ MyNum, Factor และ Result ออกมา
            • เราจะประกาศตัวแปรโดยคั่นด้วย Comma ซึ่งจะเขียนต่อกันหรือแยกบรรทัดก็ได้ (ปกติจะนิยมแยกบรรทัด แบบตัวอย่างด้านล่างมากกว่า)
            • ตัวแปรนึงสามารถอ้างอิงค่าจากตัวแปรอื่นได้ เช่น Result อ้างอิง MyNum, Factor
            • ในที่นี้เราเรียกเอาตัวแปร Result ออกมาหลัง in เพื่อแสดงผลใน Query นี้
            let
                MyNum = 5, Factor=100,  Result=MyNum*Factor
            in
                Result
            let
                MyNum = 5,
                Factor=100,
                Result=MyNum*Factor
            in
                Result

            จะเห็นว่าผลลัพธ์ของ Query ได้ออกมาเป็นเลข 500 (ใช่… Power Query สามารถให้ผลลัพธ์ออกมาเป็น Value เดี่ยวๆ ได้ ไม่จำเป็นต้องออกมาเป็นตารางเสมอไป)

            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 322

            ตัวอย่าง 2 : ใส่ Comment

            • ใส่ Comment ด้วยการพิมพ์ // แล้วตามด้วย Comment ที่ต้องการ
            • ทุกอย่างที่ใส่เป็น Comment จะไม่ถูกประมวลผล
            • ประโยชน์คือเอาไว้กลับมาดู Code ตอนหลัง หรือส่งให้คนอื่นทำงานต่อจะได้ไม่งง
            • แล้ว comment เลห่านั้นจะมาโผล่เป็นตัว i ใน applied steps ด้วย
            let
                MyNum = 5,   //ให้ MyNum มีค่าเป็น 5
                Factor=100, //ให้ MultiplyFactor มีค่าเป็น 100
                Result=MyNum*Factor //เอาตัวแปร 2 ตัวคูณกัน 5*100 = 500
            in
                Result //ผลออกมา 500
            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 323

            ตัวอย่าง 3 : เริ่มรู้จัก List

            • สร้าง List ได้ด้วย List = {item1,item2,item3}
            • item แต่ละอันใน List จะเป็นข้อมูลประเภทไหนก็ได้ ไม่เหมือนกันก็ได้
            • เราสามารถอ้างอิงเอาค่าใน List ออกมาได้ด้วย ListName{index} โดยที่ index ของ item ตัวแรก คือ 0
            • สร้าง MyList1 ที่ประกอบไปด้วยเลข 4, 29, 70
            • สร้าง MyList2 ที่ประกอบไปด้วยข้อความว่า ant, bat, cat
            • สร้าง MyList3 ที่ประกอบไปด้วยเลข 1-20
            • สร้าง MyList4 โดยรวม List1, List2 และ List3 เข้าด้วยกันด้วย &
            • อ้างอิงข้อมูล item ที่เป็นคำว่า bat ใน MyList4 ด้วย List{เลขindex}
            let
                MyList1 = {4, 29, 70},
                MyList2 ={"ant", "bat", "cat"},
                MyList3 = {1..20},
                MyList4 = MyList1&MyList2&MyList3
                
            in
                MyList4
            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 324

            ลองอ้างอิง item ที่เป็นข้อความว่า bat ด้วย MyList{4} (ตัวที่ 5 คือ index 4 )

            ตัวอย่าง 4 : แปลงค่าในคอลัมน์ของตารางให้กลายเป็น List

            ที่สำคัญเลย หากเรามีตารางอยู่ เราสามารถอ้างอิงค่าในคอลัมน์ใดคอลัมน์นึ่งให้เป็น List ได้ด้วยการเขียนสูตรในรูปแบบ TableName[ColumnName] เช่น

            สมมติว่าเรามีตารางแบบนี้ ชื่อว่า MyData แล้วเราเอาดึงค่าไว้ในตัวแปร MyTable

            Order-IDDateProduct-NameUnit-CostUnit-PriceQuantity
            ORD-0011/4/2023Laptop5007003
            ORD-0022/4/2023Monitor1001505
            ORD-0034/4/2023Keyboard203510
            ORD-0045/4/2023Mouse10257
            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 325

            หาเราเขียนสูตรว่า =MyTable[Quantity] เราจะได้ข้อมูลในคอลัมน์ Quantity ออกมาเป็น List

            let
                MyTable = Excel.CurrentWorkbook(){[Name="MyData"]}[Content],
                GetQty = MyTable[Quantity]
            in
                GetQty
            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 326

            ตัวอย่าง 5 : ฟังก์ชันสำเร็จรูป

            • ดูฟังก์ชันมาตรฐานทั้งหมดได้ที่นี่
            • ฟังก์ชันใน MCode จะมีเยอะมากๆ วิธีเลือกฟังก์ชัน คือ มันมักจะขึ้นต้นด้วยประเภทข้อมูลที่เกี่ยวข้อง ตัวอย่างเช่น….
              • ฟังก์ชันเกี่ยวกับ Number เช่น Number.Abs, Number.Mod, Number.Sign,
              • ฟังก์ชันเกี่ยวกับ Text เช่น Text.Lower, Text.Contains, Text.Replace, Text.Split, Text.Combine
              • ฟังก์ชันเกี่ยวกับ List เช่น List.Sum, List.Min, List.Max, List.Distinct, List.Select, List.Transform, List.Zip
              • ฟังก์ชันเกี่ยวกับ Table เช่น Table.ColumnNames, Table.TransformColumnNames, Table.SelectRows, Table.SelectColumns
              • ฟังก์ชันเกี่ยวกับ Date เช่น Date.AddDays, Date.AddMonths, Date.ToText, Date.IsInCurrentYear
            let
                MyTable = Excel.CurrentWorkbook(){[Name="MyData"]}[Content],
                GetQty = MyTable[Quantity],
                ListMin = List.Min(GetQty), // คำนวณค่าน้อยสุดของ List
                ListMax = List.Max(GetQty), // คำนวณค่ามากสุดของ List
                Result= ListMax-ListMin //เอาค่ามากสุด ลบ น้อยสุด
            in
                Result
            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 327

            จะเห็นว่าผลลัพธ์ออกมาได้เลข 7

            ตัวอย่าง 6 : สร้าง Custom Function ง่ายๆ

            • นอกจากฟังก์ชันมาตรฐาน ที่มีมาให้เยอะแยะแล้ว เรายังสามารถสร้างฟังก์ชันขึ้นมาใช้เองได้ด้วย ซึ่งประโยชน์คือเอาไว้ใช้ร่วมกับการ Transform ที่มีความซับซ้อนมากขึ้น
            • สร้าง Custom Function ขึ้นมาได้ ด้วย (parameter) => function expression
            • ฟังก์ชั่นจะรับค่า parameter แล้วส่งเข้าไปใน expression
            (MyList as list) as number =>
            let
                Result = List.Sum(MyList) // คำนวณผลรวมของ List
            in
                Result //แสดงผลรวมออกมา
            • สมมติเราตั้งชื่อ Query ว่า GetRange มันจะกลายเป็นชื่อฟังก์ชัน
            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 328
            • เราจะสามารถเรียกใช้ Function ได้ด้วย =GetRange(List)
            • เช่น = GetRange(Listที่ต้องการ) จะได้ 7 เช่นเดียวกันกับตัวอย่างก่อนหน้านี้
            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 329

            ตัวอย่าง 7 : เข้าใจคำว่า each

            • each คือ ตัวย่อของ (_) =>
            • ซึ่งเกิดจากการที่ใช้ฟังก์ชันมี input ตัวเดียว แล้วใช้ชื่อตัวแปร ว่า _
            • นั่นคือ (x) => x+5 จะเขียนได้ว่า
              • (_) => _ + 5 ซึ่งจะเขียนได้ว่า
              • each _ + 5
            • นั่นคือ (x) => Text.Replace(x,”A”,”B”) จะเขียนได้ว่า
              • (_) => Text.Replace(_,”A”,”B”) ซึ่งจะเขียนได้ว่า
              • each Text.Replace(_,”A”,”B”)

            ตัวอย่าง 8 : เข้าใจ each ในบริบทของแต่ละแถวของตาราง

            เวลาที่เรากดสร้างคอลัมน์ใหม่ ไม่ว่าจะใช้เครื่องมือ หรือใช้ฟังก์ชัน Table.AddColumn ก็ตาม มันมักจะมีคำสั่ง each โผล่มาด้วยเสมอ เช่น เรากด Add Custom Column เพื่อสร้างคอลัมน์ยอดขายจากตาราง จะได้แบบนี้ (ผมแก้ชื่อคอลัมน์เป็น UnitPrice จะได้อ่านง่ายๆ)

            สูตรใน Custom Column เขียนว่า

            [UnitPrice]*[Quantity]
            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 330

            แต่สูตใน Step นั้นจริงๆ ออกมาว่า

            = Table.AddColumn(#"Removed Columns", "Revenue", each [UnitPrice]*[Quantity])

            จริงๆ แล้ว มันคือการย่อของ

            = Table.AddColumn(#"Removed Columns", "Revenue", each _[UnitPrice]*_[Quantity])

            ซึ่งคือการย่อของ

            = Table.AddColumn(#"Removed Columns", "Revenue", (_)=> _[UnitPrice]*_[Quantity])

            นั่ยแปลว่าจริงๆ แล้ว ฟังก์ชัน Table.AddColumn มี input ของฟังก์ชันคือ _ แล้วถ้าเราทดสอบว่ามันคืออะไร โดยให้เขียนสูตรว่า _ ไปเลย

            เราจะพบว่า _ ใน Table.AddColumn ก็คือ Record ของข้อมูลในแถวนั้นๆ นั่นเอง

            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 331

            ซึ่งการเข้าถึง item ใน Record ทำได้โดย RecordName[FieldName] ดังนั้นเราจึงเข้าถึงค่าของคอลัมน์ต่างๆ ได้ เช่น

            • _[Quantity] แปลว่า ให้เอา Field ชื่อ Quantity ของ Record นั้นๆ มา
            • ซึ่งเราย่อ _[Quantity] ให้กลายเป็น [Quantity] เฉยๆ ได้
            • นี่คือที่มาว่าทำไมเราอ้างอิงคอลัมน์ในแถวตัวเองได้ด้วย [ชื่อหัวตาราง]

            ตัวอย่าง 9 : แก้ชื่อคอลัมน์ จาก – เป็น _

            • แล้วเราต้องการแก้ชื่อคอลัมน์ในตาราง จากเครื่องหมาย – ให้กลายเป็น _ โดยไม่ต้องแก้ทีละอัน เราสามารถทำแบบนี้ได้
            • Table.TransformColumnNames ใช้เพื่อวน Loop แก้ชื่อคอลัมน์ในตารางทีละคอลัมน์ด้วยวิธีอะไรบางอย่างที่เราจะระบุในฟังก์ชัน ซึ่ง
              • ในที่นี้เราระบุว่า each Text.Replace(_,”-“,”_”)
            • เราสามารถใช้ Text.Replace เพื่อแทนที่ข้อความได้ (คล้ายฟังก์ชัน SUBSTITUTE ใน Excel) ซึ่งในที่นี้เราสั่งแทน “-” ด้วย “_”
            • ซึ่งโดยรวมแปลว่า ให้วน Loop ชื่อคอลัมน์ แล้วแทนค่าชื่อคอลัมน์ที่มี – ด้วย _
            let
                Source = Excel.CurrentWorkbook(){[Name="MyData"]}[Content],
                ReplaceColumnName=Table.TransformColumnNames(Source,each Text.Replace(_,"-","_"))
            in
                ReplaceColumnName
            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 332

            ตัวอย่าง 10 : เลือกเฉพาะคอลัมน์ที่มี – อยู่

            • เราใช้ Table.ColumnNames เพื่อเอาชื่อคอลัมน์ทั้งหมดในตารางออกมาเป็น List
            • ใช้ List.Select เพื่อทำให้ List เหลือเฉพาะ item ที่ตรงตาม Criteria
            • ใช้ Text.Contains ในการช่วยคัดเลือก item เพื่อส่งไป List.Select อีกที
            • ใช้ Table.SelectColumns เพื่อ ลือกเอาเฉพาะคอลัมน์ที่ต้องการจาก List ที่ทำไว้
            let
                Source = Excel.CurrentWorkbook(){[Name="MyData"]}[Content],
                OriginalColumnName = Table.ColumnNames(Source), //เอาชื่อคอลัมน์ทั้งหมดออกมาเป็น List
                RequiredColumn=List.Select(OriginalColumnName,each Text.Contains(_,"-")), //ทำให้ใน List เหลือแค่ item ที่มี -
                SelectSpecificColumn=Table.SelectColumns(Source,RequiredColumn) // เลือกเอาเฉพาะคอลัมน์ที่ต้องการจาก List
            in
                SelectSpecificColumn
            บันได 10 ขั้น เริ่มเรียนรู้ M Code ขุมพลังของ Power Query 333

            ตอนต่อไป

            หลังจากเรามีพื้นฐาน MCode จากบทความนี้แล้ว ในตอนต่อไปก็จะได้เรียนเรื่องซับซ้อนกว่านี้ซักทีครับ ใครสนใจรออ่านได้เลย ไม่นานเกินรอ

          • แนวทางฝึกฝน Excel ให้เก่งขึ้น

            แนวทางฝึกฝน Excel ให้เก่งขึ้น

            หลายคนถามว่าทำยังไงถึงจะเก่ง Excel มากขึ้น คำตอบง่ายๆ คือพยายามใช้มันแก้ปัญหาใหม่ๆ เรื่อยๆ เพราะยิ่งเจอปัญหาเยอะแล้วเราพยายามแก้ปัญหาจนผ่านไปได้ เราก็ะเก่งขึ้นเหมือนกับสู้กับ Monster เก่งๆ ในเกมจนเรา Level Up ได้นั่นแหละครับ ถ้าเราสู้แต่ Monster อ่อนๆ จะได้ Exp เยอะพอได้ไง? (คนเล่นเกมคงพอเข้าใจเนอะ)

            แต่ปัญหาคือ หลายคนไม่รู้จะเอาปัญหาจากไหนมาแก้? (ซึ่งก็อาจจะแปลกนิดนึง เพราะงานที่ตัวเองทำก็น่าจะมีปัญหาให้แก้อ่ะนะ 55) แต่ไม่เป็นไร สมมติว่างานที่ทำไม่มีปัญหายากๆ แล้วกัน…

            วันนี้ผมจะมาแนะนำว่าเราจะหาปัญหาจากไหนมาฝึก Excel แบบ Advance กันดี??

            แนะนำ Challenge จาก Excel Wizard

            คำตอบก็คือการฝึกจากโจทย์ที่มีคนมาถามใน Internet ซึ่งเผอิญทาง Excel Wizard ได้เอาโจทย์ที่น่าสนใจจากต่างประเทศมาคอยถามให้พวกเราลองแก้ รวมถึงช่วยกันแชร์วิธีการแก้ปัญหาหลายๆ แบบดู ซึ่งสามารถไปดูได้ที่ https://web.facebook.com/hashtag/excelchallenge

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 334

            ซึ่งปัญหาที่มาถามก็จะมีทั้งประเภทที่ออกแบบมาให้แก้ด้วยสูตรปกติ (เป็นตัวเลขเฉยๆ) กับแบบที่ออกแบบมาให้แก้ด้วย Power Query (มีคำว่า PQตามด้วยเลข) แต่เอาจริงๆ คนแก้จะใช้วิธีไหนแก้ก็ได้ ไม่ซีเรียสหรอก

            แต่บอกก่อนว่าโจทย์ที่ Excel Wizard นำมา post นี่เรียกได้ว่าเป็น Monster ระดับ Mini Boss ได้เลย ดังนั้นใครที่ทักษะยังน้อยอยู่อาจรู้สึกว่าตามไม่ทัน อันนี้ไม่เป็นไร ให้ลองอ่านไปก่อนนะครับ จะได้รู้ว่า Excel จริงๆ แล้วทำอะไรได้ และลึกซึ้งกว่าที่คิดแค่ไหน

            ไว้คุณฝึกฝนนพื้นฐานจยเก่งขึ้นค่อยมาลองตี Mini Boss เหล่านี้อีกทีในอนาคตก็ได้

            แก้ปัญหาไม่ได้ทำยังไง?

            หากพยายามลองแก้ปัญหา แล้วแก้ไม่ได้ไม่ต้องซีเรียส ส่วนสำคัญคือการได้พยายามลองแก้ด้วยตัวเอง

            แก้ไม่ออกไม่เป็นไร ขอให้คิดจนปวดหัวก่อน แล้วค่อยไปพัก Relax เช่น ไปอาบน้ำ รดน้ำต้นไม้ ขับรถ แล้วค่อยพยายามใหม่อีกทีภายหลัง บางทีตอนพักเราจะปิ๊งไอเดียใหม่ๆ ขึ้นมาได้เอง (การทำงานของสมองเป็นแบบนี้นะ คือต้องคิดหนักๆ ก่อน แล้วค่อยพัก ถึงจะ work)

            มาลองดูตัวอย่างการแก้โจทย์

            สมมติเอาโจทย์ที่ผมเอามาเป็น Screen Shot

            ให้ Save ไฟล์ออกมาไว้ในเครื่องตัวเองแล้วเริ่มแก้โจทย์

            เดี๋ยวผมจะลองแก้ด้วย Power Query ดูจะเป็นประมาณนี้

            ให้เลือกข้อมูลแล้วคลิ๊กขวา -> Get Data from Table/Range

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 335
            • มันจะบังคับให้สร้าง Table
              (เราไม่ต้องติ๊ก My Table has Header เพราะตัวตารางยังผิดอยู่)
            • จากนั้น Excel จะดูดข้อมูลนี้เข้าสู่ Power Query Editor

            ให้เราลบ Step Change Type ออกไปก่อน เพราะหัตารางยังไม่ถูก ไม่ควรทำ Change Type ตอนนี้

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 336

            หัวตารางต้องถูกต้องก่อน

            หลักการแก้ปัญหาของ Power Query ที่สำคัญสุดคือ “ต้องทำหัวตารางต้องถูกต้องก่อน”

            เคสนี้ผมคิดว่าเราควรจัดการเรื่องปีที่ Merge มาก่อน ดั้งนั้นผมจะ Transpose ข้อมูลลงมาก่อนแล้ว คลิ๊กขวา Fill Down ปี ที่ Column 1 ลงมา จะได้แบบนี้

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 337

            ทีนี้ผมคิดว่าเดี๋ยวพอ Transpose กลับไปจะมีปัญหาหัวตารางซ้ำ แล้วพอต้อง Unpivot ข้อมูลลงมาก็จะมีปัญหาอีก ดังนั้นผมจะพยายามสร้างหัวตารางใหม่ให้ไม่ซ้ำ โดยผมจะ Add Index Column ขึ้นมาก่อนดังนี้

            ทำการ Merge Columns (ในเมนู Trannsform) กับ Column1 และ Index เข้าด้วยกันด้วยตัวคั่นแปลกๆ เช่น |

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 338

            จากนั้น Transpose กลับไปจะได้แบบนี้

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 339

            คราวนี้เราจะ Promote Header แต่ว่าต้องเอาบรรทัดสุดท้ายขึ้นไปก่อน ดังนั้นก็ไปที่ Transform -> Reverse Row แล้วค่อย Promote Header แล้วลบ Change Type ออกไปซะ จะได้แบบนี้

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 340

            ที่นี้ให้เราคลิ๊กขวา Column แรกแล้ว Unpivot Other Columns ลงมาให้หมด จะได้แบบนี้

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 341

            จากนั้นให้ Split Attribute ด้วย Delimiter คือ | จะได้แบบนี้

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 342

            ถ้าหัวตารางไม่มีปัญหาแล้ว อะไรก็ง่ายละ

            คราวนี้ให้ลบ Attribute.2 ออก แล้วเลือก คอลัมน์ Attribute.1 แล้ว Transform -> Pivot Columns โดยเอา Value เป็นคอลัมน์ value แบบนี้

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 343

            แล้วเราก็จะได้ผลลัพธ์แบบที่โจทย์ต้องการดังนี้

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 344

            สรูป M Code ที่ระบบ Gen ออกมาให้ (เรายังไม่ได้เขียนเองเลย)

            let
                Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
                #"Transposed Table" = Table.Transpose(Source),
                #"Filled Down" = Table.FillDown(#"Transposed Table",{"Column1"}),
                #"Added Index" = Table.AddIndexColumn(#"Filled Down", "Index", 0, 1, Int64.Type),
                #"Merged Columns" = Table.CombineColumns(Table.TransformColumnTypes(#"Added Index", {{"Column1", type text}, {"Index", type text}}, "th-TH"),{"Column1", "Index"},Combiner.CombineTextByDelimiter("|", QuoteStyle.None),"Merged"),
                #"Transposed Table1" = Table.Transpose(#"Merged Columns"),
                #"Reversed Rows" = Table.ReverseRows(#"Transposed Table1"),
                #"Promoted Headers" = Table.PromoteHeaders(#"Reversed Rows", [PromoteAllScalars=true]),
                #"Unpivoted Other Columns" = Table.UnpivotOtherColumns(#"Promoted Headers", {"Sales Man|0"}, "Attribute", "Value"),
                #"Split Column by Delimiter" = Table.SplitColumn(#"Unpivoted Other Columns", "Attribute", Splitter.SplitTextByDelimiter("|", QuoteStyle.Csv), {"Attribute.1", "Attribute.2"}),
                #"Changed Type" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Attribute.1", Int64.Type}, {"Attribute.2", Int64.Type}}),
                #"Removed Columns" = Table.RemoveColumns(#"Changed Type",{"Attribute.2"}),
                #"Pivoted Column" = Table.Pivot(Table.TransformColumnTypes(#"Removed Columns", {{"Attribute.1", type text}}, "th-TH"), List.Distinct(Table.TransformColumnTypes(#"Removed Columns", {{"Attribute.1", type text}}, "th-TH")[Attribute.1]), "Attribute.1", "Value", List.Sum)
            in
                #"Pivoted Column"

            วิธีอื่นๆ เช่น การใช้สูตร

            ถ้าไปดู Post นั้นจะเห็นว่ามีวิธีการทำมากมาย เช่น Excel Wizard ก็แสดงวิธีใช้สูตรขั้นสูงแบบนี้ ซึ่งทำเอาไว้ 2 วิธีด้วยกัน

            1

            =LET(y,SCAN(,C2:Q2,LAMBDA(a,v,IF(v,v,a))),u,UNIQUE(y,1),HSTACK(B2:B9,MMULT(--C2:Q9,N(u=TRANSPOSE(y)))))

            2

            =LET(z,C2:Q9,REDUCE(B2:B9,C2:Q2,LAMBDA(a,v,LET(x,CHOOSECOLS(z,COLUMNS(C2:v)),IF(v,HSTACK(a,x),HSTACK(DROP(a,,-1),TAKE(a,,-1)+x))))))

            จะเห็นว่าวิธีของ Excel Wizard เขียนสูตรได้สั้นมาก แต่ก็มีความลึกซึ้งสูงมากด้วยเช่นกัน พวกเราสามารถศึกษาวิธีที่ตัวเองชอบได้เลยครับ

            เดี๋ยวผมจะอธิบายวิธีแรกของ Excel Wizard ให้

            เผื่อเพื่อนๆ จะได้เรียนรู้เทคนิคเจ๋งๆ หลายอย่างที่อยู่ในสูตรนี้ด้วย

            เราลองมาคลี่สูตรดูก่อน จะพบว่ามันมีการประกาศตัวแปรด้วย LET แบบนี้

            =LET(
            y,SCAN(,C2:Q2,LAMBDA(a,v,IF(v,v,a))),
            u,UNIQUE(y,1),
            HSTACK(B2:B9,MMULT(--C2:Q9,N(u=TRANSPOSE(y))))
            )

            ซึ่งแปลว่า

            • ให้สร้างตัวแปร y ขึ้นมาด้วยสูตร SCAN(,C2:Q2,LAMBDA(a,v,IF(v,v,a)))
            • สร้างตัวแปร u ด้วยสูตร UNIQUE(y,1)
            • แล้วสุดท้ายให้คำนวณสิ่งนี้ออกมา
              HSTACK(B2:B9,MMULT(–C2:Q9,N(u=TRANSPOSE(y))))

            เรามาทำความเข้าใจทีละส่วนกันครับ

            คำสั่ง SCAN เป็น Lambda Helper Function ตัวนึงซึ่งสามารถสร้าง Array ที่เป็นการคำนวณสะสม (สะสมในที่นี้ไม่ได้แปลว่ารวมเสมอไป) โดยแสดงผลสะสมระหว่างทางด้วย ใครยังไม่คุ้นกับ LAMBDA และ Helper ลองดูบทความนี้ก่อนครับ

            Excel Wizard สร้างตัวแปร y ออกมาเพื่อสร้างเลขปีให้มันไม่ว่าง ดังนี้

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 345

            วิธีทำงานคือ ให้ Scan รับค่า C2:Q2 (Range ปีที่มีแหว่งๆ) เข้ามา แล้วส่งเข้า LAMBDA โดยที่ในนั้นมี Parameter 2 ตัวคือ a กับ v (ตั้งมาแทน Accumulator กับ Value)

            • โดยที่ a (Accumulator) คือค่าสะสมที่ได้แต่ละขั้น แต่ว่าแรกสุดจะเอามาจาก Initial Value ซึ่งในที่นี้ปล่อยว่างไว้ คือเป็น 0
            • โดยที่ v (Value) คือค่า Array ที่รับเข้ามาจาก Scan ในที่นี้คือ C2:Q2 ซึ่งคือ {2014,0,0,2015,0,0,0,0,2016,0,0,0,2017,2018,0}
            • โดยที่ LAMBDA มีการสั่งให้คำนวณโดย IF(v,v,a) แปลว่า ถ้า v มีค่าที่ไม่ใช่ 0 จะเป็น TRUE ก็คือให้ผลเป็น v นั้นๆ นอกนั้นให้มีผลเป็น a (ค่าผลลัพธ์เดิม)
            • ขั้นตอนจะเริ่มที่ a = initial = 0 ก่อน แล้วทำ step ถัดไป
            • ขั้นแรก v=2014 ทำให้ IF(v,v,a) เป็น TRUE เลยเอา v คือ 2014 ซึ่งผลที่ได้เก็บไว้ใน a
            • ขั้นสอง v=0 ทำให้ IF(v,v,a) เป็น FALSE เลยเอา a คือ 2014 ซึ่งผลที่ได้เก็บไว้ใน a ต่ออีก
            • ขั้นสาม v=0 ทำให้ IF(v,v,a) เป็น FALSE เลยเอา a คือ 2014 ซึ่งผลที่ได้เก็บไว้ใน a ต่ออีก
            • ขั้นสี่ v=2015 ทำให้ IF(v,v,a) TRUE เลยเอา v คือ 2015 ซึ่งผลที่ได้เก็บไว้ใน a ต่ออีก
            • ทำไปเรื่อยๆ…. จนครบทุกกค่าใน Array ก็จะได้ผลเป็นปีแบบไม่แหว่ง

            ต่อไป u คือ การเอาค่าแบบไม่ซ้ำออกมา จาก v ก็จะได้ดังนี้ อันนี้ง่าย

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 346

            อันสุดท้าย HSTACK(B2:B9,MMULT(–C2:Q9,N(u=TRANSPOSE(y))))

            แปลว่าให้เอา Range B2:B9 มาต่อข้างๆ (Horizontal Stack) ด้วยอีกก้อน
            คือ MMULT(–C2:Q9,N(u=TRANSPOSE(y)))

            ซึ่งตัวที่ซับซ้อนคือก้อนหลังนี่แหละ

            –C2:Q9 คือการบังคับให้ทุกช่องเป็นตัวเลข อันนี้ไม่มีอะไรซับซ้อน

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 347

            u=TRANSPOSE(y) คือ การเทียบว่า u เท่ากับ TRANSPOSE(y) หรือไม่?

            หมายเหตุ : หากเราเอา Array 2 ตัวที่อยู่คนละทิศกัน คือแนวตั้ง กับ แนวนอน มาทำ operation กัน ผลจะออกมาเป็นตาราง cross กัน

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 348

            พอเอา N มาครอบมันก็จะแปลง TRUE/FALSE เป็น 1 กับ 0 แบบนี้ (เพื่อให้ใช้กับ Matrix Multiplication ได้) ซึ่งจริงๆ อันนี้ใช้ — แทน N ก็ได้นะ

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 349

            พอใช้ MMULT จับ Matrix 2 ตัวคูณกัน

            MMULT(–C2:Q9,N(u=TRANSPOSE(y)))

            จะได้แบบนี้

            การคูณ Matrix จะจับคู่ Range แถวของตารางแรก คูณ Range คอลัมน์ของตารางสอง ทีละคู่ๆ แล้วหาผลรวม

            2014 เกิดจาก 2014*1 + 0*1 + 0*1 + 2015*0 +0*0+….

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 350

            183 เกิดจาก 54*1 + 37*1 + 92*1 + 37*0 +40*0+….

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 351

            ทำไปเรื่อยๆ จนครบทุกช่อง จะได้แบบนี้ ซึ่งพูดง่ายๆ คือ ในแต่ละแถวจะรวมเลขของปีเดียวกันเข้าด้วยกันนั่นเอง

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 352

            พอเอาไป HSTACK ต่อกับ B2:B9 ก็จบเลย จะได้แบบนี้

            แนวทางฝึกฝน Excel ให้เก่งขึ้น 353

            นี่แค่โจทย์เดียวนะ…

            ลองคิดดูว่าการลองฝึกโจทย์ข้อเดียวจาก Challenge ของ Excel Wizard ยังได้เทคนิคหลายอย่างเยอะแยะขนาดนี้ หากคุณไปลองฝึกฝนกับโจทย์ข้ออื่นๆ อีก จะได้ความรู้เยอะขนาดไหน!!

            ใครที่เริ่มจัดการกับ Mini Boss เหล่านี้ด้วยตนเองได้ ก็จะเริ่มตี Mini Boss ตัวอื่นได้ด้วยเช่นกัน และสึดท้ายจะตี Boss ใหญ่ที่คุณจะเจอในอนาคตได้อย่างแน่นอนครับ

          • วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4]

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4]

            ในตอนก่อนหน้านี้เราได้เรียนรู้วิธีการทำงานของ Prompt เบื้องต้นไปแล้ว แต่หากใครลองใช้ไปซักพักจะพบว่า ถึงเราจะกำหนด Prompt ยังไง บางครั้งก็ไม่สามารถบรรยายให้แบบมีท่าทางตามที่ต้องการได้เป๊ะๆ อยู่ดี…

            แล้วเราจะทำยังไงถึงจะสามารถกำหนดท่าทางได้ดั่งใจล่ะ?

            ทางออกของสิ่งนี้คือการใช้ Extension ที่ชื่อว่า ControlNet มาช่วยนั่นเอง

            หมายเหตุ : บทความนี้เขียนตอน ControlNet V1 แต่ตอนนี้มีตัว V1.1 ออกมาแล้ว ใครสนใจไปดูอันใหม่ได้เลย

            อะไรคือ ControlNet?

            ControlNet นั้นเป็น Extension หรือส่วนเสริมที่จะช่วยให้เราสามารถควบคุมผลลัพธ์ของรูปให้ได้ดั่งใจมากขึ้น ซึ่งมีอยู่หลาย Model แต่ละ Model มีความสามารถในการควบคุมที่แตกต่างกัน ผมจะขอแนะนำ Model ที่สำคัญมากๆ 2 ตัว นั่นก็คือ OpenPose และ Depth

            OpenPose

            • Preprocessor : สามารถอ่านท่าทางจากรูป ให้ออกมาเป็นมนุษย์ก้าง OpenPose ได้
            • Model : สามารถใช้รูปมนุษย์ก้าง OpenPose มาควบคุมท่าทางแบบที่ Generate ออกมาได้
            • แปลว่าถ้าเรามีรูปมนุษย์ก้างอยู่แล้ว ให้เลือก Preprocessor เป็น None นะ
            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 354

            Depth

            • Preprocessor : สามารถอ่านความตื้นลึกของรูปต้นฉบับออกมาเป็น Depth Map ได้
            • Model : สามารถใช้ความตื้นลึก Depth Map มาควบคุมสิ่งที่ Generate ออกมาได้
            • แปลว่าถ้าเรามี Depth Map อยู่แล้ว ให้เลือก Preprocessor เป็น None นะ
            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 355

            สำหรับตัวอย่าง Model อื่นๆ ลองดูได้จากที่นี่

            วิธีการติดตั้ง ControlNet

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 356
            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 357
            • จากนั้นเอา Model ที่โหลดมา ไปไว้ใน Folder \stable-diffusion-webui\models\ControlNet ให้เรียบร้อย
            • ให้ไปตั้งค่าใน Setting เพิ่มเติม เพื่อให้เราสามารถใช้ ControlNet ได้หลาย Model พร้อมๆ กัน โดยให้ตั้งค่า Multi ControlNet: Max models amount เป็น 2-3 แล้วแต่ความต้องการ (ผมตั้งเป็น 2)

            แล้ว Apply Setting แล้วกด Reload UI (ปุ่มข้างๆ) ซะ

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 358

            Tips : การใช้ ControlNet จะกิน VRAM เพิ่มขึ้น (ยิ่งถ้าใช้ Multi-ControlNet ยิ่งหนัก) ดังนั้น ถ้าใครมีปัญหา VRAM ไม่พอ คือ Gen รูปไม่ผ่าน ให้ลองตั้งค่า ดังนี้ใน webui-user.bat ดูนะครับ

            เพิ่ม medvram หรือ lowvram เข้าไปใน COMMANDLINE_ARGS เช่น

            set COMMANDLINE_ARGS=--xformers --medvram --autolaunch

            วิธีใช้งาน ControlNet

            เราสามารถใช้งาน ControlNet ได้ทั้งในส่วนของ txt2Img และ Img2Img ซึ่งผมจะสอนในส่วนของ txt2img ก่อนน่าจะเข้าใจง่ายกว่า (เดี๋ยวเราจะไปใช้ใน img2img ช่วงท้ายบทความ)

            วิธีการก็คือให้เราใส่ Prompt ไปตามปกติ แต่อย่าใส่คำอะไรที่จะไปขัดให้มันขัดกับท่าทางที่เราจะควบคุมด้วย ControlNet

            เช่น สมมติว่า Generation Data ที่เราทำไว้แบบนี้ ด้วย Model ChillOutMix อันเดิม

            (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (1 korean girl), smile, (standing on the beach:1.3), (long hair:1.3), (wearing white tank top and red Shorts:1.3)
            Negative prompt: paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)),((text))
            Steps: 30, Sampler: DPM++ SDE Karras, CFG scale: 7, Seed: 2637412235, Size: 512x768, Model hash: fc2511737a, Model: chilloutmix_NiPrunedFp32Fix

            ซึ่งจะออกมาได้ภาพนี้

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 359

            แต่เราอยากจะได้ Pose ของภาพนี้

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 360

            หมายเหตุ : เราควร Crop รูปให้มีอัตราส่วนตามรูปที่เราต้องการจะ Gen ด้วย จะได้การควบคุมที่คุณภาพดีกว่า เช่น รูปที่ผมจะ Gen มีอัตราส่วน 2:3 ก็ควรจะ Resize/Crop ให้เป็น 2:3 ด้วย แต่เผอิญต้นฉบับเรา 2:3 อยู่แล้วเลยไม่ต้องทำอะไร

            วิธีทำคือ ให้ ไปที่ ControlNet แล้ว Enable แล้ว Upload รูปต้นฉบับ แล้วตั้งค่าตามนี้

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 361

            แล้วตั้งค่า Width กับ Height ให้สัดส่วนเท่ารูปที่เราอยากจะ Gen

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 362

            จากนั้นกด Generate ใหม่ เราจะได้ผลลัพธ์ออกมา 2 อัน คือ รูปที่ Gen กับ OpenPose ที่ใช้ควบคุมรูป

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 363
            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 364

            เราจะเห็นได้ว่า OpenPose สามารถควบคุมท่าทางโดยรวมได้ดีพอสมควรเลย อาจจะเหลือเรื่องมือที่ยังไม่ได้ควบคุมด้วย OpenPose ปกติ (จริงๆ สามารถเปิด Setting ให้ Detect มือได้ แต่ผมว่ามันยังไม่ค่อย Work เท่าไหร่)

            ในตอนนี้ปัญหาหลักๆ ของภาพที่ได้คือ หน้ายังไม่ค่อยสวย และมือเบี้ยว ซึ่งเรื่องหน้าเดี๋ยวเราจะแก้ด้วยการ Inpaint ทับทีหลัง ดังนั้นเราจะมาแก้ปัญหาเรื่องมือก่อน

            ส่วนเรื่องมือเดี๋ยวเราจะไปแก้ด้วย ControlNet อีก Model นึงก็คือ Depth นั่นเอง แต่ว่าเราจะใช้ Depth มาจัดการแค่ในส่วนของมือเท่านั้น ซึ่งเราจะใช้ Depth Library มาช่วยควบคุม ControlNet ตัว Depth Model

            ใช้งาน Depth Library

            เข้า tab Depth Library แล้ว Add Background Image เป็นรูปอะไรก็ได้ที่จะทำให้เรารู้ว่ามือควรอยู่ตรงไหน เช่นจะใช้รูปมนุษย์ก้างก็ได้

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 365

            หรือจะเอารูปที่เรา Gen ไปแล้วมือเบี้ยวมาเลยก็ได้เห็นภาพชัดดี (แต่ให้ปรับ size ให้ตรง)

            จากนั้นให้ Add Hands เข้าไปแล้วปรับตำแหน่งให้เหมาะสม แล้วกด Save รูป Depth เอาไว้ (หรือจะกดส่งเข้า ControlNet เลยก็ได้)

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 366

            ซึ่งจะได้ Depth อันนี้มา

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 367

            เอา Depth มาใส่ ControlNet

            จากนั้นกลับมาที่ ControlNet แล้วเลือกอีก Tab นึง แล้วโหลดรูป Depth ตามรูปนี้ (อย่าลืม Enable และเลือก Preprocessor เป็น None เพราะเรามี Depth แล้ว)

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 368

            จากนั้นลองกด Generate ดู เราจะได้ภาพนี้ออกมา ซึ่งจะพบว่ามือไม่หงิก (ซักเท่าไหร่) แล้ว 55

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 369

            ทำยังไงให้ภาพใหญ่ขึ้น ละเอียดขึ้น เป๊ะขึ้น?

            ทีนี้เราลองกด Hires-Fix แบบ Latent ดูซัก 1.5 เท่า แล้ว Generate ดูว่าผลลัพธ์ยัง ok อยู่หรือไม่?

            หมายเหตุ : การใช้แบบ Latent คือมันจะ Gen รูปตามปกติก่อนรอบนึง แล้วคล้ายๆ ว่าจะ Denoise แล้ว Upscale ต่อโดยเติมรายละเอียดเข้าไป ซึ่งผลที่ได้จะได้รายละเอียดที่ดีขึ้นเยอะเลย (แต่มีสิทธิ์ที่ภาพจะเพี้ยนไปจากเดิมก่อนที่จะ Upscale)

            แต่ถ้าเรา Upscale แบบอื่น แม้ภาพจะไม่เพี้ยน แต่ว่าจะไม่ได้รายละเอียดของภาพที่ดีครับ

            ในทีนี้ผมตั้ว Upscale แบบ Latent 1.5 เท่า และ Denoising strength เป็น 0.5 (แบบ Latent ถ้า denoise น้อยกว่านี้ภาพจะเน่า) ผลจะได้ที่ภาพใหญ่โดยรวมชัดเจนขึ้น แต่มือดันหงิกขึ้น

            หมายเหตุ : ปัจจุบัน ControlNet อาจมีปัญหากับ HiresFix อยู่บ้าง โดยเฉพาะ Model ที่ต้องการความเป๊ะเช่น Depth หรือ Canny แต่สำหรับ OpenPose ไม่ค่อยมีปัญหาอะไร

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 370

            ดังนั้นสำหรับเคสนี้หากกด Hires Fix แบบ Latent จะยังไม่ ok เท่าไหร่ บางทีก็ยังจบงานไม่ได้ ( แต่ถ้ามองว่ามือแบบนี้ ok แล้วก็จบอ่ะนะ 55)

            แก้ปัญหายังไงดี?

            ดังนั้นถ้าหากต้องการทำให้มือเป๊ะกว่านี้ ตอนนี้มี 3 ทางเลือก คือ

            1.Gen รูปที่ความละเอียดสูงขึ้นแต่แรกเลย เช่น 768 x 1152 (แต่ผลลัพธ์อาจเปลี่ยนจากเดิมพอสมควร และจะเริ่มมีบางอย่างดูแปลกๆ เช่น ฉากหลัง สีกางเกง แถมมือก็ยังแปลกอยู่ดี 55)

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 371

            2. ใช้การ Upscale แบบที่ไม่ใช่ Latent เช่น ESRGAN_4X (แต่ในที่นี้อัป 1.5x) ภาพที่ได้อาจจะไม่มีรายละเอียดมากนัก แต่จะไม่เพี้ยนจากรูปเดิมเลย เช่น ของผมจะได้แบบนี้ ซึ่งก็ดู ok นะ (แม้มือจะเพี้ยนไปบ้าง)

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 372

            3. ใช้การ Inpaint ช่วยทำงานต่อจากรูป Upscale มือหงิกนี่แหละ โดยกดปุ่ม Send to Inpaint ต่อได้เลย

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 373

            ในที่นี้ผมจะทำจากภาพมือหงิกในแบบที่ 3 ให้ดูว่าแก้ยังไง?

            การใช้ Inpaint เพื่อแก้รายละเอียดภาพ

            พอกด Send to Inpaint แล้วผลลัพธ์ที่เราทำจะถูกส่งมาหน้า Inpaint ของ img2img ซึ่ง Inpaint คือเครื่องมีที่สามารถแก้รูปเฉพาะส่วนที่ต้องการได้ ซึ่งดีมากๆ เพราะเรากำหนดจุดโดยการระบายสีดำเพื่อ Mask พื้นที่ได้ตามต้องการ

            ให้ใช้ Prompt ดังนี้ เพื่อแก้ส่วนมือ

            (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), ( korean girl hand)

            ซึ่งให้เราใช้ Negative Prompt ดังนี้ (อันเดิม)

            paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)),((text))

            แล้วระบายสีส่วนมือที่จะแก้ แล้วตั้งค่าตามนี้ (ในเคสนี้เราเลือก Inpaint Area เป็น Whole Picture แล้วกำหนดขนาดเต็มรูป 768×1152 เพื่อให้สอดคล้องกับขนาดของ Depth ใน Control Net)

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 374

            จากนั้นตั้งค่า ControlNet ของการ Inpaint เป็น Depth ตามนี้

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 375

            พอ Generate รูปจะได้ตามนี้ ซึ่งคราวนี้มือกลับมาตรงแล้ว (ถ้ายังไม่ดีให้แก้ seed เป็น random แล้วให้มัน gen ออกมาหลายๆ รูปแล้วเลือกรูปที่ดี)

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 376

            ทีนี้ให้เราจะแก้ตรงลูกหน้าต่อโดยกดปุ่ม Send to Inpaint เพื่อเอาผลลัพธ์ที่แก้มือแล้วไปใช้ต่อเป็นจุดตั้งต้นอีกรอบนึง

            ทีนี้ถ้าจะแก้หน้าตา เราอาจแก้ prompt เป็น

            (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2), (korean girl face, smile)

            ระบายสีดำตรงหน้าอย่างเดียว แล้วตั้งค่าตามนี้ (ในเคสนี้เราเลือก Inpaint Area เป็น Only Masked เพื่อให้ให้มัน Render แค่ตรงสีดำๆ ด้วยขนาดที่กำหนด เช่น 768×768)

            แล้ว Denoise บางๆ หน่อย ไม่ต้องเยอะมาก (เช่น 0.2-0.4) เพื่อให้ผลลัพธ์ไม่เพี้ยนจากเดิมเยอะจนเกินไป

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 377

            แล้วไม่ต้อง Enable ControlNet แล้ว พอ Generate ออกมาจะได้แบบนี้ ถ้าคิดว่าแบบนี้พอใจแล้วก็จบได้เลย ถ้าไม่พอใจก็ปรับค่านิดๆ หน่อยๆ แล้ว Gen เพิ่มเพื่อเลือกรูปที่ ok

            Denoise 0.25

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 378

            Denoise 0.3

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 379

            เท่านี้ก็น่าจะได้ผลลัพธ์ที่ดีขึ้นแล้วล่ะ!

            สามารถเป๊ะได้มากกว่านี้อีกนะ

            การใช้ ControlNet อ่าน OpenPose จากรูป หรือการใช้ Depth Library เอามือมาแปะ เป็นวิธีที่ง่ายและสะดวก แต่ผลลัพธ์อาจไม่เป๊ะตามต้องการ เพราะอาจไม่มี Pose หรือ Depth ที่เราต้องการจริงๆ อยู่ก็ได้ วิธีที่จะได้สิ่งที่ต้องการมีอีก 2 ระดับ (อันที่ 2คือวิธีที่เป๊ะที่สุด)

            1.ใช้ Extension ชื่อ PoseX จัด OpenPose ได้ดั่งใจ แถมหมุนมุมกล้องได้ด้วย (ข้อดีคือง่าย แต่ข้อเสียคือจัดท่าแล้วเพี้ยนง่ายเช่นกัน เพราะมันลากจุดไปไหนก็ได้ เดี๋ยวมือยาวเป็นแม่นาคเลย)

            วิธีกำหนดท่าทางแบบให้ได้ดั่งใจด้วย ControlNet ใน Stable Diffusion [Part4] 380

            2. ใช้ Blender เพื่อใช้ Model 3D (โหลดมาได้เลย ไม่ต้องปั้นเอง) มาทำ OpenPose กับ Depth เพื่อกำหนดท่าทาง และมือแบบเป๊ะๆ ใครสนใจไปดูคลิปนี้ได้ คุณทีทำไว้ดีมากๆ ละเอียดมาก แบบไม่เคยใช้ Blender ก็ทำตามได้ แถมภาษาไทยด้วย

            ตอนต่อไป

            ในตอนต่อไป เราจะมาเรียนรู้วิธี Train Model LoRA ขึ้นมาใช้เอง เพื่อให้สามารถกำหนดหน้าตา เสื้อผ้า หรือสไตล์ของรูปที่เราจะ Gen ออกมาได้ รับรองว่าเด็ดสุดๆ แน่นอน คอยติดตามได้เลยครับ

            รวมบทความ Stable Diffusion

            • วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3]

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3]

              จากเนื้อหาตอน 1 ที่ได้พูดถึงวิธีลงโปรแกรม และ ตอน 2 วิธีโหลดและเรียกใช้ model ไปแล้ว เราจะพบปัญหาว่า ถึงจะใช้ Model ที่สวย แต่ถ้าหากสั่ง Prompt และตั้งค่าไม่ถูกต้อง ภาพก็จะออกมาไม่สวยอยู่ดี ดังนั้นในบทความนี้ผมจะมาสอนเทคนิคที่จะทำให้ภาพที่ออกมามันสวยขึ้นกันครับ

              ก่อนอื่นเรามาทำความเข้าใจเรื่องของ Prompt ก่อน ว่าหลักการสำคัญมีอะไรบ้าง?

              ในบทความนี้ผมจะใช้ Model หลัก (Checkpoint) คือ Chilloutmix-Ni-pruned-fp32-fix และ LoRA ชุดนวดไทย (ที่ผมทำขึ้นมาเอง) นะครับ (แต่ตอนแรกจะยังไม่ได้เรียกใช้งาน LoRA)

              วิธีการสั่ง Prompt

              เราต้องบรรยายรูปที่เราต้องการเข้าไป บรรยายอะไรก็จะเห็นสิ่งนั้น (แต่ขึ้นกับว่า Model ที่เราเลือกมา ได้ train สิ่งที่เราต้องการมารึเปล่าด้วย)

              ซึ่งหลักการคือใส่เป็น Keyword สำคัญก็พอครับ เพราะถึงใส่เป็นประโยคไป AI มันจะตีความหมายที่แท้จริงจาก Prompt ของเราอีกทีอยู่ดี (ใช่ มันตีความได้นะ…) และยิ่งใส่เยอะยิ่งควบคุมยาก ให้ค่อยๆ ใส่ทีละขึ้นจะได้เข้าใจมากขึ้น

              ก่อนอื่นผมจะขอ fix เลข seed เป็นค่านึงก่อน ทุกคนจะได้ออกมาเหมือนกัน ในที่นี้ผมขอให้ seed เป็นเลข 999 แล้วกัน

              จากนั้นใส่ Prompt ว่า beautiful girl แล้ว Gen ออกมาเลย โดยไม่แก้ค่าอื่นแล้ว (นอกจาก seed กับ model) ผลจะออกมาแบบนี้

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 381
              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 382
              beautiful girl

              สิ่งแรกที่ควรกำหนด คือ สัดส่วนภาพ

              เราควรจะคิดแต่แรกเลย ว่าภาพที่เราต้องการจะเป็นสี่เหลี่ยมจตุรัส หรือจะเป็นภาพแนวตั้ง แนวนอน ซึ่ทั้งหมดก็ขึ้นอยู่กับว่าเราจะทำภาพอะไรด้วย ถ้าเป็นภาพ Portrait บุคคล ภาพแนวตั้งก็น่าจะดีกว่าแนวนอนเป็นต้น แต่ถ้าภาพวิวแนวนอนอาจดีกว่า (แต่ถ้าเป็นคนในท่านอนอาจจะภาพแนวนอนดีกว่าก็ได้)

              ดังนั้นผมจะสร้างภาพแนวตั้งขึ้นมา ซึ่งสมมติผมจะใช้สัดส่วน 2:3 ซึ่งเพื่อไม่ให้มัน Gen ช้าเกินไป ผมจะทำให้ด้านที่ยาวสุด (แนวตั้ง) ยังคงเป็น 512 px อยู่ ดังนั้นแนวนอนก็ควรจะเป็น 2/3*512 = 341.33 ซึ่งถ้าเลื่อน slider ก็จะลงล๊อคเลขที่ละ 8 ก็จะเป็นเลข 344 x 512 นั่นเอง (คร่าวๆ ไม่ต้องเป๊ะก็ได้) ถ้ากด Generate จะได้แบบนี้

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 383
              beautiful girl

              อย่างไรก็ตามถ้าเราอยากจะ Gen รูปที่มีความหมายอย่างที่เราต้องการ เช่น เราต้องการสาวหูแมว เราก็อาจใส่ไปเลยว่า

              beautiful girl, cat ear

              ซึ่งผลจะออกมาแบบนี้

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 384
              beautiful girl, cat ear

              สมมติว่าหน้าออกมาเหมือนภาพวาดเกินไป เราอยากให้มันเป็นรูปสมจริง เราก็ใส่ Keyword เช่นคำว่า photorealistic เพิ่มเข้าไป หรือถ้าอยากให้ภาพละเอียดขึ้นก็ใส่ ultra highres เข้าไป เช่น

              beautiful girl, cat ear, photorealistic, ultra highres

              ออกมาได้แบบนี้

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 385

              สมมติว่ามันดูโป๊เกินไป เราก็แาจจะระบุเสื้อผ้าให้น้องเค้าหน่อย เช่น จะใส่ชุดชมพู เลย เขียนว่า pink shirt ได้ออกมาแบบนี้

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 386
              beautiful girl, cat ear, photorealistic, ultra highres, pink shirt

              แต่ผมดันออกมาชมพูด้วย สมมจิเราอยากได้ผมสีน้ำตาล ก็ใส่ brown hair ไป

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 387
              beautiful girl, cat ear, photorealistic, ultra highres, pink shirt,brown hair

              หมายเหตุ : การสลับที่ keyword อาจจะให้ผลต่างกันได้นะ (แม้จะใช้ seed เดิม)

              ทีนี้มีอีกเรื่องนึงที่จะแนะนำคือ การลองเปลี่ยน Sampling Method ดูก่อน เผื่อผลลัพธ์จะดีขึ้นอีก

              ลองเปลี่ยน Sampling Method

              เจ้า Sampling Method นั้นขึ้นอยู่กับ Model ที่เลือกใช้ด้วย ว่าตัวไหนเหมาะกับ Model ของเรา

              วิธีที่จะทดสอบ Sampling Method หลายๆ ตัวพร้อมกัน โดยไม่ต้องไล่เปลี่ยนเองได้ คือ ใช้เครื่องมือ x/y/z plot ด้านล่าง ตามรูป (ผมใส่ในแกน y มันจะได้ทำในแนวตั้ง) แล้วกด Generate (ตรงนี้เพื่อนๆ ไม่ต้องทำก็ได้ เพราะผมทำให้ดูแล้ว)

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 388

              สิ่งที่มันทำคือลองสร้างรูปตาม Sampler แต่ละตัว ซึ่งผลออกมาดังนี้ (ทุกอันยัง seed เดียวกันนะ คือ 999)

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 389
              (บางตัวภาพเละเพราะ Steps ไม่พอ แต่บางตัว Steps แค่ 20 ก็ทำงานได้)

              และถ้าให้เจ๋งขึ้นไปอีกลองใส่ Negative Prompt ดู

              อะไรคือ Negative Prompt?

              มันก็คือสิ่งที่เราไม่อยากได้ในรูป เช่น ถ้าเราอยากให้มันดูไม่ใช่รูปวาด รูปการ์ตูน งั้นเราใส่คำนี้ลงไปใน Negative Prompt เลย (เป็นกล่องอยู่ใต้ Prompt ปกติ)

              paintings, sketches, 3d, cartoon, anime

              และถ้าผมลอง x/y/z plot ใหม่ จะได้ผลแบบนี้ ซึ่งจะเห็นว่าหลายอันค่อนข้างสมจริงขึ้นมามากๆ แล้ว

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 390

              สมมติว่าผม ok กับ DPM++SDE ผมก็เอาอันนี้มาทำงานต่อ

              ถ้าอยากได้ผลลัพธ์ที่ Perfect ที่สุดจาก Sampling Method ที่เลือก อาจจะลองแก้ค่า CFG Scales และ Steps ให้ค่าเปลี่ยนแปลงไปด้วย คราวนี้ผมจะ plot ทั้งแกน x และ y เลย

              โดยผมจะลอง x/y/z plot โดยแก้

              • CFG Scales : อยู่ที่ประมาณ 6-8 โดยซอยเพิ่มเลขทีละ 0.5
                • กำหนดแบบนี้ 6-8(+0.5)
              • Steps : ให้สร้าง 10 ภาพระหว่างเลข 15-30
                • กำหนดแบบนี้ 15-30[10]
              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 391

              ลอง Gen ดู ถ้าไม่ไหว ให้ลดการเปลี่ยนแต่ละตัวแปรให้จำนวนน้อยลง หรือค่อยๆ ทำทีละแกนนะ (หรือจะแค่ลองดูผลจากที่ผมทำก็ได้ ไม่ต้อง Gen เอง เพราะเยอะจัดเลย)

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 392

              ที่นี้ชอบอันไหน เลือกอันนั้นได้เลย

              ทีนี้ผมจะไม่ plot xyz แล้ว (เลือก Script เป็น None) แต่จะใช้เลข Step 26, CFG 7 แล้วกัน

              Gen ทีเดียวหลายๆ รูป ด้วย Batch Size, Batch Count

              แล้วจะให้มัน Gen ออกมาหลายๆ รูป โดยเปลี่ยน Batch Size, Batch Count

              • Batch Count : จำนวน Batch ที่จะ Gen (ถ้ามีหลาย Batch ก็จะรันต่อๆ คิวกัน)
              • Batch Size : จำนวนรูปที่จะ Gen พร้อมๆ กันในแต่ละ Batch

              ตามหลักแล้ว เพิ่ม Batch Size จะ Gen เร็วกว่า แต่จะกิน Memory หนักกว่า อันนี้ต้องลอง

              สมมติผมตั้ง Batch Count เป็น 3 และ Batch Size เป็น 1 (แปลว่า Gen ทีละรูป แต่ทำ 3 รอบ) จะได้ 3 รูป โดย seed เริ่มที่ 999 ไล่ไปเรื่อยๆ

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 393
              seed 999
              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 394
              seed 1000
              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 395
              seed 1001

              แต่ถ้าผมตั้ง Batch Count เป็น 3 และ Batch Size เป็น 2 (แปลว่า Gen ทีละ 2 รูป แต่ทำ 3 รอบ) จะได้แบบนี้ ซึ่งจะพบว่าภาพที่ได้มันต่างไปโดยสิ้นเชิงเลย! ทั้งๆ ที่ seed ไล่ตั้งแต่ 999

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 396
              999 (Batch Size 2)
              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 397
              1000 (Batch Size 2)
              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 398
              1001 (Batch Size 2)
              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 399
              1002 (Batch Size 2)
              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 400
              1003 (Batch Size 2)
              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 401
              1004 (Batch Size 2)

              วิธีดูข้อมูลจากรูปที่ Gen ออกมา

              ถามมว่าผมรู้ได้ไงว่าแต่ละอัน seed อะไร? นั่นก็เป็นเพราะ

              วิธี 1 : คลิ๊กที่รูปที่ Output ออกมาสดๆ แล้วดูข้อมูลseed ได้

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 402

              วิธี 2 : ลากรูปไปในแถบ PNG Info

              นอกเหนือจากวิธีคลิ๊กที่รูปเพื่อดู seed แล้ว จริงๆ เรายังมีอีกวิธีที่สามารถดึงการตั้งค่าทั้งหมดจากรูปที่เรา Gen ออกมาได้ด้วยนะ
              โดยเอารูปไปในแถบ PNG Info (Browse ไฟล์ หรือ ลากลงก็ได้)

              เช่น ผมลากอันนี้มาได้

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 403

              จากนั้นมันจะขึ้นข้อมูลของรูปนั้นๆ มา ซึ่งเราสามารถกด send to txt2img ได้เลย

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 404

              มันก็จะเอาข้อมูล prompt และ setting ทั้งหมดของรูปนั้นไปใช้ได้ ซึ่งจะ gen ออกมาใหม่ โดย gen หลายๆ รูปด้วย seed ถัดๆ ไปก็ได้

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 405

              วิธี 3 : เอาจาก Generated Data

              ส่วนวิธีที่เด็ดอีกอัน คือ การ Copy Generated Data จากคนอื่น เช่น Civitai

              สมมติเราเข้าไปใน LoRA Thai Massage Dress (ชุดนวดไทย) แล้วเจอรูปที่ชอบ ที่มีตัว i อยู่ (นั่นคือ รูปนั้นยังฝัง Generated Data ไว้)

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 406

              มันจะขึ้นข้อมูลว่ารูปนั้น Gen มาด้วยวิธีไหน ให้เรากดที่ Copy Generation Data ปุ่มฟ้าๆ ข้างล่าง

              ซึ่งจะได้ข้อมูลนี้มา

              (best quality:1.4), (ultra highres:1.2), (photorealistic:1.4), (8k, RAW photo:1.2),(full head:1.4), (southeast asian girl), (black hair),(red Thai Massage uniform :1.5),<lora:thaiMassageDress_v2:0.7>
              Negative prompt: paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)),((text:2)),((head band)),,((head accessory))
              Size: 344x512, Seed: 2980161819, Model: chilloutmix_NiPrunedFp32Fix, Steps: 20, Sampler: DPM++ 2M Karras, CFG scale: 7, Model hash: fc2511737a, Hires upscale: 2, Hires upscaler: Latent, Denoising strength: 0.5

              ให้เราเอาไป paste ใน Prompt ของเรา แต่ให้ลบของเดิมออกให้หมดก่อน โดยกดปุ่มถังขยะ

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 407

              แล้ว Paste Generation Data ลงไปใน Prompt เดียวยาวๆ เลย แล้วค่อยกดลูกศรฟ้า ข้างๆ ถังขยะ

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 408

              มันจะโปรย Data กระจายไปจุดต่างๆ รวมถึงการเลือก Sampler, Step, Seed ทุกอย่างเลย

              ว้าววว สบายสุดๆ ถูกใจสาย Copy Paste อ่ะสิ อิอิ

              วงเล็บ และตัวเลขคืออะไร?

              วงเล็บ และตัวเลข มันเอาไว้กำกับน้ำหนักของ Keyword ซึ่งมีความหมายดังนี้

              เราระบุน้ำหนักได้ในรูปแบบ (keyword:น้ำหนัก)

              • ถ้า keyword ไม่ระบุอะไรเพิ่มเลย และไม่มีวงเล็บด้วย คือน้ำหนัก 1
              • (keyword:1.5) แปลว่า ให้น้ำหนัก 1.5 เท่าของปกติ
              • (keyword:0.5) แปลว่า ให้น้ำหนักครึ่งนึงของปกติ (ลดน้ำหนักลงไป 2 เท่า)
              • การใส่ (keyword) มีค่าเท่ากับน้ำหนัก 1.1 หรือ (keyword:1.1)
              • การใส่ ((keyword)) มีค่าเท่ากับ (keyword:1.21) ซึ่งเกิดจาก 1.1*1.1
              • การใส่ [keyword] คือ ลดน้ำหนักลงไป 1.1 เท่าของปกติ = (keyword:0.909)
              • รายละเอียดอ่านได้ในนี้

              ลอง Gen ดูสิ

              พอเรา Gen อีกทีก็จะได้แบบที่ผมทำนี้เลย (ถ้าใครทำแล้วไม่ได้ผลแบบนี้ แสดงว่าขาด Model หรือ LoRA ที่ Prompt นั้นใช้ ก็ต้องไปหาโหลดมาก่อนนะ ใครโหลดไม่เป็นกลับไปอ่านตอน2)

              วิธีสั่ง Prompt และตั้งค่าใน Stable Diffusion ให้รูปสวยโดนใจ [Part3] 409
              รูปนี้เกิดจากการ Copy Prompt ที่ผมบอกมา Gen แต่ต้องมี Model รองรับนะ

              เช่น ใน Prompt จะเห็นว่ามีเขียน LoRA ไว้ดังนี้

              • <lora:thaiMassageDress_v2:0.7>

              แปลว่า คุณต้องมี LoRA ชื่อ ThaiMassageDress โดยชื่อไฟล์ที่ save มาก็ต้องเป็น thaiMassageDress_v2 และ Gen โดนใช้ Model หลักคือ chilloutmix_NiPrunedFp32Fix ถึงจะได้ผลแบบเดียวกับผมนะ

              ศึกษา พัฒนา ปรับปรุง

              Copy Paste งานสวยๆ ที่ชอบแล้วอย่าลืมเอามาศึกษา พัฒนา ปรับปรุง

              อย่างถ้าคุณชอบผลงานรูปสาวใส่ชุดนวดไทยที่ผมทำ คุณก็ลองดูว่าผมระบุอะไรบ้าง? เช่น สีชุด สีผม สีหน้า และบางรูปผมก็ระบุสถานที่ด้วย รวมถึง effect ของแสงต่างๆ ก็สามารถระบุได้มากมาย

              คุณลองหาสไตล์ที่ตัวเองชอบ แล้วสร้างผลงานให้โลกนี้ได้เห็นกันครับ ว่าใครๆ ก็ทำได้ (ถ้าพยายามพอ! ผมเองหัดแค่ 2 อาทิตย์ก็ทำได้แล้ว)

              ไม่เชื่อคุณลองไปส่องเพจ AI ใน Link นี้ดูสิ เค้าสร้างผลงานได้สวยๆ ทั้งนั้น

              ตอนต่อไป

              ในตอนต่อไป เราจะมาเรียนรู้วิธีปรับแต่งรวมถึงควบคุมรูปให้ได้ผลลัพธ์ดั่งใจมากขึ้น เช่น การควบคุมท่าทางของแบบเป็นต้น ใครสนใจพลาดไม่ได้เด็ดขาด!

              รวมบทความ Stable Diffusion

              • วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2]

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2]

                จากที่ผมได้อธิบายวิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ ในบทความที่แล้ว ผมได้ทิ้งท้ายไว้ว่า การที่จะทำให้รูปออกมาสวยได้ มันมีหลายปัจจัย เช่น ตัว Model หลัก/ Model เสริม / Text Prompt /Negative Prompt ที่ใส่เข้าไป/ Setting ต่างๆ ในหน้าจอ / ส่วนเสริม Extension เจ๋งๆ / รวมถึงโชค !!

                จะให้อธิบายหมดเลยรวดเดียวคงไม่ไหว ดังนั้นในบทความตอนนี้ผมจะขออธิบายและแนะนำส่วนของ Model หลัก และ Model เสริม (เช่น ถ้าใครสงสัยว่า LoRA คืออะไร? อ่านบทความนี้จบคุณจะรู้จักมันมากขึ้นแน่นอน) ก่อน ว่าควรจะใช้ Model แบบไหนมันถึงจะออกมาสวยได้ เพื่อไม่ให้เสียเวลา ไปเริ่มกันเลย!

                Model คืออะไร?

                สำหรับ Section นี้อาจดู Nerd หน่อยๆ แต่ผมอยากให้ลองอ่านดูจะได้เข้าใจหลักการคร่าวๆ ของ AI ด้วย ซึ่งถ้าเข้าใจเรื่องนี้ คุณจะตั้งค่า parameter ต่างๆ รวมถึงกำหนด Prompt ได้ดีขึ้นด่วยนะ

                ปกติแล้วการทำงานของ AI วาดรูป หลักการโดยทั่วไปคือ การเปลี่ยน Noise ตั้งต้น (จุดๆ ที่ดูไม่รู้เรื่อง) ให้กลายเป็นรูปสิ่งต่างๆ ที่เราระบุได้

                แล้ว Noise ตั้งต้นที่ว่าเนี่ย ปกติจะถูกกำหนดด้วยเลข Seed ด้วย ซึ่งค่า Default ใน Stable Diffusion ตั้งเป็นเลข -1 (ติดลบ 1) ซึ่งเป็นการ Random แต่เราสามารถกำหนดเป็นเลขอะไรก็ได้

                นั่นแปลว่า ถ่า seed เหมือนกัน และใส่ prompt (คำบรรยาย) เดียวกัน แล้วส่งเข้า Model AI ตัวเดียวกันเราก็จะได้ภาพออกมาเหมือนกันเลย (แต่ที่หลายคนสร้างภาพออกมาไม่เหมือนคนอื่น เพราะเลข Seed ปกติจะ Random ไง)

                หลักการทำงานของ AI (คร่าวๆ)

                แล้ว AI มันเปลี่ยน Noise ให้กลายเป็นรูปได้ยังไง? หลักการใหญ่คือ สิ่งที่เรียกว่า Diffusion Model ซึ่งขออธิบายแบบง่ายๆ ว่า ปกติ Computer มันใส่ Noise เพิ่มเข้าไปในรูปได้อยู่แล้ว เราจึงสามารถ Train มันให้ปลด Noise ออกจากรูปได้เช่นกัน

                ค่อยๆ ปลดออกทีละ Steps เดี๋ยวจนในที่สุด ปลด Noise ไปเรื่อยๆ ก็จะกลายเป็นรูปปกติได้ เจ๋งง

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 410

                อย่างไรก็ตามไอ้จุด Noise ตั้งต้นมันเละจนดูไม่รู้เรื่องหรอก (ดู noise ขวาสุดในรูปบนดิ) ดังนั้นตอนมันจะปลดกลับมา มันอาจจะได้กลับคืนออกมาเป็นรูปอะไรก็ได้ทั้งนั้น ซึ่งขึ้นอยู่กับ Prompt หรือคำบรรยายของเรา (เพราะ Model ไม่ได้ถูกเทรนแค่รูปแมวอันนี้ แต่มันเทรนจากคลังรูปมหาศาล อย่างน้อยก็ตัวนี้ https://laion.ai/blog/laion-5b/ ซึ่ง 5b คือ 5 billion หรือ 5 พันล้านรูป!! )

                ไอ้ความสามารถในการปลด Noise ออก แล้วกลายเป็นรูปตาม Prompt หรือคำบรรยายของเราเนี่ยแหละ รวมถึงการที่มันจะรู้จัก keyword คำว่าอะไร รู้จักรูปอะไรบ้าง วาดสไตล์ไหนได้บ้าง มันถูกฝึกฝนแล้วเก็บไว้ในสิ่งที่เรียกว่า Model หมดแล้ว

                หลักการจริงๆ มันซับซ้อนกว่านี้เยอะ เช่น ข้อความที่เราใส่ มันจะถูกตีความในแบบที่ AI เข้าใจก่อน คือมันรู้ด้วยว่าคำไหนใกล้เคียงกันกับคำไหนในเชิงความหมายจริงๆ (ไม่ใช่แค่จำตัวอักษร)

                แถมหลักการเทรนรูปของ AI มันล้ำมากนะ คือคล้ายๆ ว่ามันจะแบ่ง AI เป็น 2 ตัวแข่งกัน ตัวนึงพยายามสร้างรูปให้เหมือนตาม Prompt มากที่สุด ส่วนอีกตัวจะต้องจับผิดให้ได้ว่ารูปที่ได้มา มาจาก AI ทำหรือคือภาพเฉลยจริง ดังนั้นพอมันแข่งไปเรื่อยๆ จนมันเริ่มแยกภาพไม่ออกว่าอันไหนจริงอันไหนปลอม สุดท้ายเราจะได้ AI ที่ Gen ภาพเก่งมากๆ ออกมานั่นก็คือ AI วาดรูปนี่แหละ

                รวมถึงใน Stable diffusion เองก็มีเทคนิคเพิ่มจาก AI วาดรูปทั่วไปอีก นั่นคือเทคนิคที่เรียกว่า Latent Diffusion Model การแปลงรูปให้อยู่ในรูปแบบที่เล็กลงจนจัดการง่าย (Encode) แล้วสามารถแปลงรูปที่ถูกดัดแปลงให้กลับมาใหญ่เหมือนภาพดั้งเดิมได้อย่างน่าอัศจรรย์ (Decode)

                Stable Diffusion จึงเป็น AI วาดรูปที่สามารถรันได้บนคอมพิวเตอร์คนทั่วไปได้ (แทนที่จะต้องรันใน Server โหดๆ แบบ Dall-E หรือ Midjourney) ประกอบกับความ Open Source อีกทำให้คนจำนวนมากเข้าถึงมันได้ ซึ่งนี่แหละคือปัจจัยพลิกเกม ที่ทำให้ Stable Diffusion พัฒนาเร็วและมี Model หลากหลายมากๆ ในปัจจุบัน (และน่าจะพัฒนาแบบเร็วโหดมากๆ ต่อไปด้วย)

                ทั้งหมดนี้น่าจะเป็นหลักการสำคัญเบื้องต้นที่คุณควรจะรู้ไว้ว่า AI มันวาดรูปได้ยังไง ซึ่งแน่ๆ คือ มันไม่ได้ Copy รูปของใครเก็บไว้ตรงๆ มันแค่เรียนรู้วิธีสร้างรูปขึ้นมาจาก Noise หรือจุดที่ดูไม่รู้เรื่องเท่านั้น (ถ้าใครอยากรู้หลักการของ AI ละเอียดกว่านี้ จะพยายามสรุปให้อีกที )

                สำหรับประเด็นที่ว่า การเทรน AI นั้นเอารูปมาจากไหน ต้องบอกว่าอันนี้แล้วแต่ Model ที่ใข้เลย บาง Model ก็อาจเอารูป Public ที่ถูกต้องมา บาง Model ก็อาจไปเอารูปศิลปินมาใช้ทื่อๆ (ซึ่งอันนี้ไม่ควร) ผมจะขอไม่พูดถึงประเด็นทางกฎหมายและศีลธรรมตอนนี้ละกันนะครับ

                พอเข้าใจหลักการคร่าวๆ แล้วเราไปดูวิธีโหลด Model กันดีกว่า

                ที่โหลด Model ของ Stable Diffusion

                ผมแนะนำ 2 ที่ คือ

                เดี๋ยวในบทความนี้เราจะเน้นโหลดจาก CivitAi นะครับ เพราะมี Model สวยๆ เพียบเลย (แต่ถ้าใครอยากโหลดจาก Hugging Face ผมมีวิธีแปะไว้ท้ายบทความนะ)

                Model หลัก (Checkpoints)

                • Model หลัก เราจะเรียกมันว่าไฟล์ Checkpoints
                  • Model แบบนี้ Train มาจากเครื่องมือที่ชื่อว่า Dreambooth ซึ่งจะเทรนแบบนี้ได้ต้องมี GPU ที่ทรงพลังมากๆ ถ้าคอมพ์เราทำไม่ไหว อาจต้องไปเช่า GPU บน Cloud เอา
                  • อย่างไรก็ตาม เราสามารถเอา Model ที่มีอยู่ มาผสมผสานกันตามน้ำหนักที่เราระบุ เพื่อสร้างเป็น Model ใหม่ได้ด้วย เรียกว่าการ Merge Checkpoints ซึ่งอันนี้เครื่องคอมพ์ปกติสามารถทำได้ไม่ยากเลย (เครื่องผมก็กดได้)
                • โดยหลักการคือ Train ทั้ง Model ให้รู้จักเรื่องที่เราอยากสอน ทำให้วิธีนี้ไฟล์ใหญ่มากๆ แต่คุณภาพรูปที่ได้จะถือว่าดีที่สุดเลย
                • ไฟล์ Model หลัก 1 อันอาจใหญ่ประมาณ 3-7 GB ได้เลย ถ้าใครเน็ตไม่แรงก็ต้องรอหน่อย
                • เราจะ Download Model จากที่ไหนก็ได้ มันจะเป็นไฟล์ นามสกุล .SafeTensor หรือ .Ckpt (ถ้ามีให้เลือกทั้งคู่ให้เลือกนามสกุล SafeTensor เพราะปลอดภัยมากกว่า เพราะอีกตัวมีสิทธิ์ที่จะถูกฝัง Script แปลกๆ มาได้ )
                • ไม่ว่าจะโหลด Model จากที่ไหนมา ให้เอาไปไฟล์ที่โหลดเสร็จแล้วไปไว้ที่ Folder ที่ชื่อว่า stable-diffusion-webui\models\Stable-diffusion

                Model เสริม

                ในส่วนของ Model เสริมนั้น หน้าที่คือ เป็นเหมือน Model ที่เป็น Add in ที่สามารถใช้เสริมเข้าไปใช้ร่วมกับ Model หลักได้ โดยสามารถใช้ Model เสริมหลายๆ ตัวร่วมกันได้ด้วย!!

                แต่เจ้า Model เสริมเนี่ยจะมีหลายแบบพอสมควร แต่ผมจะแนะนำแค่ 2 แบบที่เจ๋งสุดดังนี้

                • LoRA (Low-Rank Adaptation) :
                  • เป็น Model เสริมที่ให้คุณภาพดีมากๆ ถึงมากที่สุด โดยที่ขนาดไฟล์จะอยู่ที่ประมาณ 100 MB ++ ซึ่งเล็กมากเมื่อเทียบกับ Checkpoints
                  • ตัวนี้คือเทคโนโลยีใหม่สุด จิ๋วแต่แจ๋ว
                  • โหลดเอาไปไว้ใน stable-diffusion-webui\models\Lora
                • Textual Inversions :
                  • เป็น Model เสริมที่ขนาดเล็กสุดๆ (ขนาด 10-100 KB) แต่คุณภาพสู้ LoRA ไม่ได้
                  • โหลดเอาไว้ใน stable-diffusion-webui\embeddings

                ที่เจ๋งสุดๆ คือ Model เสริมทั้ง 2 ตัวนี้ เราสามารถใช้ Computer ตัวเอง เทรนมันขึ้นมาใช้งานเองได้ เช่น เอารูปหน้าตัวเองใส่เข้าไปเยอะๆ มันก็จะสามารถ Generate หน้าของเราได้เลย แถมเอาไปใช้กับ Model หลักอื่นๆ ได้อีก เช่น จะเป็นภาพแนวการ์ตูน แต่หน้าเป็นหน้าเราก็ได้ สุดยอดมั้ยล่ะ!!

                ถ้าใครอยากรู้วิธีเทรน สามารถดูคลิปพวกนี้ได้ (คลิป Eng นะ แต่ทำตามได้เลย ผทก็ดูคลิปนี้แล้วทำได้เลย)

                วิธีโหลดจาก Civitai

                • เข้าเว็บ https://civitai.com/ แล้ว Sign Up/Login ให้เรียบร้อย
                • จากนั้น Filter หา Model ประเภทที่ต้องการ เช่น Model หลักเลือก Checkpoint Model เสริมก็อาจจะเลือก LoRA
                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 411
                • ปกติอันที่อยู่บนๆ ดาวเยอะๆ รีวิวเยอะๆ ก็มักจะดี
                • แต่ควรเข้าไปสำรวจรูปประกอบทั้งจากคนสร้าง Model และ คนที่มา Review Model นั้นๆ ด้วยว่าใช่แบบที่ชอบหรือไม่
                • ถ้าอยากจะโหลดตัวไหน ให้กดปุ่ม Download ด้านขวา

                ลองโหลด Model มาทดสอบเล่น

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 412

                อย่างไรก็ตาม แต่ละ Model อาจมีเรื่อง license ที่แตกต่างกัน ยังไงลองดูด้วยนะครับ ว่าเค้ายอมให้ใช้ Model เค้าทำอะไรบ้าง?

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 413
                License ของ ChilloutMix

                วิธีการเรียกใช้งาน Model

                พอเรา Copy Model ลงใน Folder ตามที่ผมแนะนำแล้ว คือ เอาไว้ใน Folder เหล่านี้

                • Checkpoints (หลัก) : stable-diffusion-webui\models\Stable-diffusion
                • LoRA : stable-diffusion-webui\models\Lora
                • Textual Inversion : stable-diffusion-webui\embeddings

                จากนั้น Run ไฟล์ webui-user.bat แล้วเข้า http://127.0.0.1:7860/ เพื่อเข้าหน้า Web UI ตามปกติเลย

                ลอง Prompt แบบปกติก่อน

                โดยใช้ Model มาตรฐานที่เราโหลดมาแต่แรก คือ sd-v1-4.ckpt

                ให้เราลองใส่ Prompt ดังนี้

                beautiful thai girl wearing thai university uniform

                โดยให้ลองเลือก seed เป็นเลข 123456 เพื่อทำให้ภาพได้เหมือนของผมเป๊ะๆ (แล้วจะได้ไม่เกิดการ Random ภาพไปเรื่อยๆ ไม่เชื่อลอง Generate อีกรอบดูได้ จะได้อันเดิม)

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 414

                จะพบว่าภาพที่ได้มันแย่มาก… ทีนี้เราลองมาเปลี่ยน Model กัน

                วิธีเลือก Model หลัก

                ที่มุมซ้ายบนของหน้าจอ ให้กดปุ่มสีฟ้าเพื่อ Refresh Checkpoint จากนั้นเลือกตัวที่ต้องการ เช่น ผมเลือก chilloutmix_NiPrunedFp32Fix.safetensors

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 415

                พอเลือกแล้วต้องรอมันโหลดแป๊ปนึง แล้วลอง Generate ใหม่ โดย prompt เดิม seed เดิม

                เราจะพบว่าภาพที่ได้ดูดีขึ้นมาหน่อยแล้ว และนี่ก็จะเริ่มมีความ Sexy โผล่มานิดๆ ละ (เพราะ ChillOutMix มันถูกเทรนมาแบบนั้น) ดังนั้นการเลือก Model นั้นมีผลต่อภาพอย่างแน่นอน

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 416

                ถ้าสังเกต การเลือก Model จะเป็น Drop Down List แปลว่า เราเลือก Model หลักได้ตัวเดียวนะ

                วิธีเรียกใช้ LoRA

                ให้กดที่ Show Extra Networks แล้วเลือก Lora ที่ต้องการ (Search ได้) ดังนี้

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 417

                โปรแกรมจะเพิ่ม Text Prompt ให้เราเอง โดยจะมี <lora:ชื่อไฟล์:multipler> โผล่มา
                ซึ่ง multipler คือ เลข 0-1 ที่เรากำหนดได้ว่าจะให้ LoRA ส่งผลแค่ไหน

                กลายเป็น

                beautiful thai girl wearing thai university uniform <lora:thaiUniversity_v10:1>

                ถ้าลอง Generate ดู จะได้แบบนี้

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 418

                ภาพเปลี่ยนไปเล็กน้อย แต่ยังไม่ได้ชุดนักศึกษาโผล่มา ทั้งนี้อาจเป็นเพราะ LoRA นั้นๆ ต้องทำการเรียก Trigger ด้วย Keywords บางอย่างด้วย เช่น ของ Thai University Uniform จะมี Trigger Words ประมาณนี้

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 419

                ดังนั้นเราอาจลองใส่ Trigger Words เข้าไปด้วยในวงเล็บ เพื่อเพิ่มความสำคัญนิดๆ นั่นคือ น้ำหนักเท่ากับค่า 1.1 ซึ่ง

                • การใส่ (keyword) มีค่าเท่ากับ (keyword:1.1)
                • 1.1 ในนี้คือน้ำหนักในการตีความ ซึ่งเกิน 1 ได้ (ไม่เหมือน LoRA ที่ได้แค่ 1 นะ)
                • การใส่ ((keyword)) มีค่าเท่ากับ (keyword:1.21) เกิดจาก 1.1*1.1

                แต่เราไว้ลงรายละเอียดพวกนี้ในบทความถัดไปนะ

                beautiful thai girl wearing thai university uniform, (white shirt short sleeve), (black tight skirt) <lora:thaiUniversity_v10:1>

                ผลจะได้แบบนี้ ซึ่งจะเริ่มมีชุดนักศึกษาโผล่มาแล้ว

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 420

                และแน่นอนว่าเราสามารถเรียกใช้ LoRA หลายๆ อันใน Prompt เดียวกันได้ในรูปแบบต่อไปนี้

                <lora:ชื่ออันแรก:น้ำหนักแรก>,<lora:ชื่ออันสอง:น้ำหนักสอง>,...

                วิธีเรียกใช้ Textual Inversion

                เราแค่เรียกใช้โดยใส่ Trigger Words เช่นกัน

                เช่น ใส่ว่า ulzzang-6500-v1.1 หรือ (ulzzang-6500-v1.1) ก็ได้ ในที่นี้ผมใส่เป็น (ulzzang-6500-v1.1)

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 421

                นี่มันอะไรกัน??? เอิ๊กกกก…

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 422
                มี Model แต่ตั้งค่าไม่ดี = ไม่รอด

                และเราก็เรียกใช้ Textual Inversion หลายๆ อันใน Prompt เดียวกันได้เช่นกันนะ ก็ Comma คั่นแต่ละตัวไป

                ใส่ Model ครบแล้วทำไมยังไม่สวย?

                แม้ว่าจะเรียกใช้ Model หลักและรองจนครบแล้ว แต่ทำไมภาพก็ยังออกมาไม่สวย? ทั้งนี้ก็เป็นเพราะยังมีปัจจัยเรื่องอื่นนอกจาก Model อยู่อีกยังไงล่ะ? เช่น

                • การกำหนด Negative Prompt
                • การกำหนดน้ำหนักความสำคัญ เช่น (Keyword:น้ำหนัก)
                • การกำหนด Sampling Methods
                • การกำหนด Sampling Steps
                • และอื่นๆ อีกพอสมควร

                เดี๋ยวผมจะลองปรับให้ดูคร่าวๆ ว่าถ้าใส่สิ่งต่างๆ พวกนี้ (โดยลองไม่เปลี่ยนเพิ่ม Prompt ปกติ ยกเว้นเรื่อง Weight) ผลลัพธ์จะเป็นยังไง?

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 423

                เราจะพบว่าผลลัพธ์ออกมาดีขึ้นเยอะเลยครับ (ถ้าปรับรายละเอียด Prompt ดีๆ ก็จะสวยกว่านี้อีก)

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 424
                มี Model และตั้งค่าเหมาะสม = สวย

                อย่างไรก็ตาม ถึงเราตั้งค่าพวกนี้ทั้งหมด แต่ไม่ได้มีการโหลด Model อะไรมาเพิ่มเลย ก็จะผลลัพธ์ก็จะเป็นประมาณนี้ ซึ่งไม่ค่อยสวยเลย และนี่แหละคือความสำคัญของ Model ครับ

                วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 425
                ไม่มี มี Model แต่ตั้งค่าเหมาะสม = ก็ไม่รอด

                ตอนต่อไป

                สำหรับตอนต่อไป ผมจะพูดถึงปัจจัยที่เหลือ เพื่อที่จะสร้างรูปให้สวยได้ดั่งใจต่อไปครับ โดยเฉพาะเรื่องการของการเขียน Prompt และการตั้งค่าต่างๆ รวมถึงการใช้ Extension เข้าช่วยเหลือ ใครที่อยากรู้วิธีทำให้ภาพสวยขึ้น พลาดไม่ได้!!

                รวมบทความ Stable Diffusion

                  Model ที่อยากแนะนำ

                  (ไว้จะมาเพิ่มให้เรื่อยๆ นะครับ)

                  Civitai

                  Model หลัก

                  • ChilloutMix : เหมาะกับภาพแนวสมจริง สาวหน้าตาน่ารักสไตล์เน็ตไอดอลเอเชีย ซึ่งเป็นหนึ่งใน Model ที่ฮิตสุดๆ เพราะ Gen รูปได้สวยมากๆ (ต้องเปิดโหมด 18+)
                  • Protogen x3.4 (Photorealism) เป็นภาพแนวสมจริงนิดๆ ทำรูปได้หลากหลาย
                  • Realistic Vision V1.3 : เหมาะกับภาพแนวสมจริง หน้าตาแบบคนทั่วไปในชีวิตจริง ไม่ได้สวยน่ารักแบบ ChillOutMix
                  • Deliberate : ทำได้หลายแนว ค่อนข้างยืดหยุ่น
                  • Openjourney : รูปภาพสไตล์ของ MidJourney
                  • RPG : รูปแนว Fantasy สมจริง

                  LoRA

                  (พยายามอย่าเอาไป Gen ให้ผิดลิขสิทธิ์/กฏหมาย/ศีลธรรมนะ)

                  มีเฉพาะใน Hugging Face

                  • anything-v4.0 : Model ที่สามารถ Gen ภาพแนวการ์ตูน Anime ญี่ปุ่นได้ค่อนข้างดี

                  วิธีโหลดจาก Hugging Face

                  • เข้าเว็บ https://huggingface.co/models?other=stable-diffusion
                  • เลือก Model ที่ต้องการ แล้วไปที่ Tab ชื่อ Files & Version
                  • กดโหลดไฟล์นามสกุล .SafeTensor เพื่อความปลอดภัย
                  วิธีเรียกใช้งาน Model เจ๋งๆ ใน Stable Diffusion [ตอนที่2] 426
                • วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1]

                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1]

                  บทความนี้ก็เป็นการฉลอง Section ใหม่ของเว็บเทพเอ็กเซล นั่นก็คือ บทความเกี่ยวกับ AI นั่นเอง เพราะตอนนี้เทคโนโลยี AI กำลังมาแรงมากๆ (ทั้ง AI วาดรูป และ ChatGPT) ใครที่พลาดไม่ทันศึกษาอาจตกยุคหรืออาจถูกหลอกได้เลยนะ ดังนั้นใครไม่อยากพลาดความรู้ดีๆ ต้องรีบศึกษาแล้ว และไม่ช้า AI จะเข้ามาอยู่ในโปรแกรมทั่วไปอย่าง Excel มากขึ้นแน่นอน

                  เอาล่ะ เข้าเรื่อง AI วาดรูปล่ะ แม้ว่าก่อนหน้านี้ผมได้เคยเขียนบทความเกี่ยวกับการใช้งาน AI วาดรูป ตัวที่ชื่อว่า MidJourney ไปแล้ว ซึ่งมันจะเป็น AI วาดรูปที่ทรงพลังมากๆ ตัวนึง แต่ข้อเสียหลักๆ ที่ผมเจอก็คือ มันไม่สามารถ Customize ผลลัพธ์ได้ตามที่เราต้องการมากนัก (หรือได้ก็ต้องใช้ความพยายามมากๆ) ที่สำคัญคือ เราไม่สามารถเลือก Model การวาดรูปในแบบที่เราต้องการเป๊ะๆ ได้ ต้องใช้ Model ที่ Midjourney เตรียมไว้ให้เท่านั้น ซึ่งมีแค่ไม่กี่แบบ และอีกอย่างก็คือมันเสียค่าใช้จ่ายรายเดือนที่ค่อนข้างสูงเลยทีเดียว (เดือนละ 30 USD)

                  ในวันนี้ผมก็เลยมาแนะนำเครื่องมืออีกตัวนึง ชื่อว่า Stable Diffusion ซึ่งผมไปซุ่มหัดใช้งานมันมาประมาณ 1 week เต็มๆ ก็พอใช้งานมันได้ดีใช้ได้แล้วล่ะ ใช้ยากกว่า Midjourney นิดหน่อย แต่มีข้อดีมากมาย เลยจะมาแชร์ความรู้ให้ทุกคนรู้ด้วยครับ ^^

                  ซึ่ง Stable Diffusion เนี่ย ข้อดีที่ผมเห็นคือ

                  • ทำรูปได้หลากหลายสไตล์ ทั้งภาพสมจริงและการ์ตูน
                  • มี Model ให้เลือกมากมาย โหลดเพิ่มเสริมได้ แถมผสม Model เองได้อีก
                  • มัน Customize ภาพผลลัพธ์ได้ละเอียดมากๆ ถึงขนาดที่สามารถกำหนดหน้าตา ท่าทาง ชุด สถานที่ได้ทั้งหมด
                  • มีเครื่องมือเสริมและ Community เยอะแยะ ที่ช่วยให้การ Gen รูปง่ายขึ้นสะดวกขึ้น
                  • ที่สำคัญคือมันฟรี!! (บ้าไปแล้ววว)

                  ตัวอย่างรูปที่สร้างจาก AI ที่สร้างจากเครื่องมือ Stable Diffusion ซึ่งดูผ่านๆ อาจคิดว่าคนจริงก็ได้นะ

                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 427
                  Screenshot จาก Post ของเพจ https://www.facebook.com/AiAngelGallery

                  Stable Diffusion คืออะไร?

                  Stable Diffusion คือ Machine Learning Model (AI) ที่สามารถเปลี่ยนข้อความที่เราป้อน ให้กลายเป็นรูปภาพตามที่เราสั่งได้ (ถ้าใครสนใจหลักการทางเทคนิคของเจ้า AI ตัวนี้ ไว้อนาคตจะมาลองสรุปให้ฟัง)

                  โดยที่มันถูกปล่อย Code และ Model ออกมาให้คนทั่วไปใช้ฟรี โดยปล่อยมาแบบ Open Source เลย แถมยอมให้คนเอาไปต่อยอดได้อีกไม่รู้จบ

                  ทำให้ตอนนี้ Stable Diffusion มาแรงมากๆ และมีเครื่องมือเสริมและการใช้งานให้เลือกได้มากมายแต็มไปหมด (แม้แต่ Midjourney ก็เอา Model ของ Stable Diffusion ไปผสมผสานนะ ตอนหลังๆ รูปใน MJ เลยสวยขึ้นมากๆ)

                  ความดีงามสุดๆ ของ Stable Diffusion คือมี Community ขั้นสุดยอดที่มี Model ใหม่ๆ เจ๋งๆ โผล่มาเพียบ ที่โดดเด่นที่สุดคือเว็บนี้ https://civitai.com/ ซึ่งจะขอแนะนำโดยละเอียดภายหลัง

                  • ต้องชี้แจงว่าในเว็บนี้หลายๆ Model เป็น 18+ นะ แต่มันจะแบ่งประเภทชัดเจน แล้วเรา Filter เลือกได้ว่าจะแสดงเฉพาะ Model แบบไหนที่เราสนใจได้ครับ
                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 428

                  อย่างไรก็ตามจะใช้ Model ข้างบนพวกนี้ได้ จะต้องเลือกใช้วิธีการใช้งาน Stable Diffusion ที่รองรับการเพิ่ม Model แบบ Custom ได้ด้วย

                  วิธีการใช้งาน Stable Diffusion

                  การจะใช้งาน Stable Diffusion ได้เนี่ย มันมีหลายวิธี แต่ผมจะขอแนะนำ 4 วิธีแล้วกันครับ

                  ใช้ผ่าน Website ที่เค้าออกแบบมาให้ User ปกติใช้งานง่ายๆ

                  ข้อดีคือใช้งานง่ายสุดๆ แต่ข้อเสียก็คือ ปรับแต่งมากไม่ได้ ใช้ model customize แบบที่ตัวเองชอบไม่ได้

                  • https://stablediffusionweb.com/ แค่เข้าไปลองพิมพ์ prompt ก็ใช้ได้เลย ไม่ต้อง install อะไรเลยทั้งสิ้น แต่ข้อเสียคือปรับแต่งอะไรแทบไม่ได้เลย คือมี Advanced Option ที่ปรับอะไรได้น้อยมากๆ เลือก Model มาใช้ก็ไม่ได้ (ลองเข้าไปกดเล่นดูได้)
                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 429
                  • https://beta.dreamstudio.ai/dream ตัวนี้มีลูกเล่นเยอะขึ้นมาหน่อย แต่ก็เลือก Model Custom เองไม่ได้ และสุดท้ายใช้ไปเรื่อยๆ Credit จะหมด คือต้องเสียเงินอยู่ดี (ตัวนี้จะคล้ายๆ Midjourney)
                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 430

                  ลงโปรแกรมในเครื่องตัวเองไปเลย (Local Machine)

                  วิธีนี้ยืดหยุ่นสุดๆ ทรงพลังสุดๆ (ถ้าเครื่องแรง คือมีการ์ดจอ GPU ที่ดีพอ) และฟรีด้วย แต่ข้อเสียคือขั้นตอนจะยุ่งยากที่สุด เพราะต้องลงโปรแกรมในเครื่องตัวเองด้วย (ผมเองใช้วิธีนี้ครับ เดี๋ยวจะบอกวิธีลงให้ใน Section ถัดไป)

                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 431

                  แต่ข้อเสียสำคัญของวิธีนี้ที่หลายคนจะติดก็คือ Computer ที่เราใช้ควรมีการ์ดจอที่ดีพอ (ไม่งั้นมันจะรันไม่ไหว) และควรมี Harddisk ที่ความจุเยอะๆ ด้วย (เพราะอาจต้องเก็บ Model หลายๆ ตัวไว้ใช้ แต่เรื่อง Harddisk ยังไม่จำเป็นเท่า GPU การ์ดจอ)

                  ป.ล. ผมเองใช้ Computer Gaming ที่มี GPU แยก ยิ่ง GPU มี VRAM เยอะๆ จะยิ่งดี ถ้าต่ำกว่า 8 GB จะทำอะไรลำบากแล้ว

                  ใช้ผ่าน Google Collab

                  อันนี้เหมาะกับคนที่อยากจะ Customize ได้เยอะๆ แต่ไม่มีคอมพ์ส่วนตัวที่ GPU (การ์ดจอ) ดีพอ แถมรันผ่านมือถือก็ได้ด้วย ซึ่งถ้าจะใช้ Google Colab ก็จะมีให้เลือกหลายตัว (แล้วแต่ว่าเราจะใช้ code ของใครมารัน Stable Diffusion)

                  –ใช้ฟรี แต่ถ้าโควตาหมดต้องรอ reset หรือไม่ก็เสียตังเพื่อใช้ Colab Pro ซึ่งก็ต้องเสียตังอีก

                  วิธีการใช้งานพวกนี้คือต้องรัน Cell ที่เก็บ code Python เอาไว้

                  ตัวมาตรฐาน จาก Stability.ai : https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/stable_diffusion.ipynb

                  ตัวยอดนิยม web ui ของ camenduru (แนะนำ)
                  แบบไม่ลง drive : https://github.com/camenduru/stable-diffusion-webui-colab
                  แบบลง drive : https://github.com/camenduru/stable-diffusion-webui-colab/tree/drive (แนะนำ)
                  Tutorial : https://www.youtube.com/watch?v=lGco45e9o5Y

                  ตัวยอดนิยม webui ของ Linaqruf (แนะนำ)
                  https://github.com/Linaqruf/sd-notebook-collection

                  ตัวยอดนิยม web ui ของ nolanaatama (แนะนำ)
                  https://github.com/nolanaatama/sd-1click-colab
                  Tutorial : https://www.youtube.com/@nolanaatama/videos

                  ตัวปรับใช้ง่ายของ TheLastBen : (วิธีการใช้)
                  https://colab.research.google.com/github/TheLastBen/fast-stable-diffusion/blob/main/fast_stable_diffusion_AUTOMATIC1111.ipynb

                  ตัวที่ปรับแต่งหน้าตาให้ใช้งานง่าย โดย Altryne : https://colab.research.google.com/github/altryne/sd-webui-colab/blob/main/Stable_Diffusion_WebUi_Altryne.ipynb

                  ใช้ผ่าน Mobile App ชื่อ Draw Things

                  ในมือถือ/ipad/Mac จะมี App ชื่อ Draw Things ซึ่งเป็นโปรแกรมที่ทำดีมากเลย ใช้ง่าย และ Customize ได้เยอะ แต่ควรใช้ในอุปกรณ์ที่แรงพอ เช่น iPad Pro หรืออะไรพวกนี้ เพราะถ้ารันในมือถือจะช้าพอสมควร หรืออาจจะรันไม่ผ่านด้วยซ้ำ (วิธีการใช้งาน)

                  ข้อดีคือ ตัวนี้สามารถโหลด Model และ Textual Inversion จากแหล่งอื่นมาใช้ได้ (เช่น Civitai ) ส่วนข้อเสียคือ ตอนนี้ยังไม่สามารถใช้ LoRA ได้ และน่าจะทำให้เครื่องมือถือหรือ Ipad ทำงานหนักมาก ไม่แน่ใจว่าระยะยาวจะส่งผลเสียแค่ไหน

                  ตกลงแล้วลงโปรแกรมแบบไหนดี?

                  โดยสรุป คือ ไม่มีวิธีไหนที่ทำได้ง่ายๆ ฟรีๆ แบบถาวรหรือครับ เพราะแบบที่บอกว่าลงเครื่องตัวเองแล้วฟรี ยังไงก็ต้องใช้เงินซื้อการ์ดจออยู่ดีนะ (การ์ดจอดีๆ ก็หลายหมื่นอยู่ครับ) หรือจะใช้ App Draw Things คุณก็ต้องมี Tablet แรงๆ อย่าง iPad ไม่งั้นก็ไม่ไหว ซึ่งบางทีแพงกว่าคอมพ์อีก 555 หรือแม้แต่ Google Colab ในที่สุดก็จะโควต้าหมดจนต่างจ่ายเงิน

                  ดังนั้นทุกอย่างมี cost ของมันนะ อันนี้ต้องทำใจ

                  • ถ้าให้แนะนำคือ หากมีเครื่องคอมพ์ดีๆ GPU แรงๆ ให้ลงในเครื่องตัวเองครับ เจ๋งสุด
                  • ถ้าไม่มีคอมพ์แรงๆ แนะนำให้ใช้ Google Colab จะยืดหยุ่นเหมือนลงเครื่องตัวเองด้วย
                  • ถ้าใช้ Colab แล้วยากไป ให้ใช้ Draw Things ผ่าน ipad/mac

                  ทั้งหมดนี้คือความเห็นส่วนตัวครับ

                  งั้นต่อจากนี้ผมจะอธิบายวิธีที่ยืดหยุ่นสุด และเป็นวิธีที่ผมใช้จริง คือ ลงโปรแกรมในคอมพ์ตัวเองนะครับ

                  วิธีใช้งาน Stable Diffusion แบบลงในเครื่องคอมพ์ตัวเอง

                  สำหรับวิธีที่จะลง Stable Diffusion ในคอมพ์ตัวเอง (ซึ่งต้องมี GPU การ์ดจอที่ดีพอด้วย) แบบที่นิยมมากที่สุด คือใช้ผ่านเครื่องมือที่เรียกว่า Automatic 1111 Stable Diffusion Web UI (เพราะเค้าเขียนโปรแกรมให้สั่งงาน Stable Diffusion แบบใช้ง่ายให้เราแล้ว) ซึ่งในเว็บนั้นจะมีทั้งไฟล์ประกอบที่ต้องใช้ และมีขั้นตอนการลงโปรแกรมทั้งหมดให้ด้วย

                  คลิปประกอบวิธีการลงโปรแกรม

                  วิธีที่แนะนำ

                  วิธีนี้เราต้องลงโปรแกรม Python และโปรแกรม Git (แบบ install ปกติ ไม่ใช่ portable) ก่อน แล้วค่อยโหลดเจ้า Automatic 1111 อีกที ซึ่งวิธีทำตามคลิปได้เลย

                  ที่สำคัญห้ามลืมคือ ตอนกด Install ต้องติ๊กเลือก “Add Python to PATH”

                  วิธีคนขี้เกียจ

                  วิธีนี้แค่โหลดไฟล์ zip มาแล้วรันไฟล์ที่กำหนดแล้วรอ โดยไม่ต้องลง python กับ git เลยด้วยซ้ำ (มันจะโหลดให้เอง) อย่างไรก็ตามในระยะยาวผมไม่แน่นใจว่าจะมีปัญหาอะไรหรือไม่ ดังนั้นให้ใช้ในฐานะวิธีสำรองแล้วกัน

                  สำหรับเครื่อง Mac อาจมีบางอย่างต่างไปเช่นกัน ลองดูในนี้น่าจะดีกว่า

                  ขั้นตอนการลงโปรแกรม (เท่าที่จำเป็น)

                  เครื่องผมเป็นการ์ดจอที่มี GPU Nvidia นะครับ สำหรับคนที่ใช้การ์ดจอ AMD อาจต้องมีการลงโปรแกรมที่ต่างออกไปเล็กน้อย เช่น Link นี้, Link นี้

                  • ต้องลงโปรแกรม Python (3.10.x)
                    • สำคัญคือ ตอนกด Install Python ต้องติ๊กเลือก “Add Python to PATH”
                  • ลงโปรแกรม Git (แบบ install ปกติ ไม่ใช่ portable) ซึ่ง Next รัวๆ ได้
                  • สร้าง Folder ที่ต้องการ เช่น MySDFolder จากนั้นเข้าไปใน Folder
                  • เข้าไปใน navigation bar แล้ว Click ซ้าย 1 ทีแล้วพิมพ์ cmd กด enter เพื่อเข้า cmd prompt (อย่าเพิ่งช๊อค ทำตามไปก่อน 555)
                    วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 434

                  พิมพ์ใน cmd prompt ตามนี้ (ใช้ copy paste เอาได้) แล้วกด Enter

                  git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git

                  โปรแกรมจะโหลดไฟล์จาก AUTOMATIC1111/stable-diffusion ที่เรากำหนด

                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 435

                  ขั้นตอนการโหลด Model

                  ขั้นตอนนี้ข้ามได้ ถ้าเราไม่ได้โหลด Model อะไรเลย เดี๋ยวโปรแกรมมันโหลดให้เองตอนรัน webui-user.bat ที่อยู่ใน Folder ชื่อ stable-diffusion-webui

                  บอกไว้ก่อนว่าเราสามารถโหลด Model จาก Civitai มาเพิ่มได้อีกครับ ซึ่งใครๆ ก็โหลด เพราะ Model พื้นฐานของ Stable Diffusion มัน Generate ได้ไม่สวยหรอก

                  วิธีการ Run โปรแกรม Stable Diffusion Web UI

                  หลังจากติดตั้งตามขั้นตอนข้างบนแล้ว ให้ดับเบิ้ลคลิ๊กเพื่อรันไฟล์ชื่อ webui-user.bat ที่อยู่ใน Folder ชื่อ stable-diffusion-webui (ที่โปรแกรมสร้างให้นอกสุด)

                  ครั้งแรกจะมี Script อะไรโผล่มาเยอะแยะ (อย่าเพิ่งช๊อค) และจะรันนานหน่อย (ครั้งถัดไปจะเร็วขึ้น)

                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 436

                  ให้รอจนมันขึ้น

                  Running on local URL:  http://127.0.0.1:7860
                  
                  To create a public link, set `share=True` in `launch()`.

                  แบบนี้ถือว่า Install จบแล้ว

                  ลองใช้งานครั้งแรก

                  แนะนำให้คลิ๊ขวา Edit ไฟล์ web user-ui.bat โดยเพิ่ม parameter

                  • –autolaunch เอาไว้ทำให้รันแล้วไม่ต้องไปคลิ๊ก URL เอง ก็สะดวกดี
                  • –xformers สำหรับคนที่มีการ์ดจอ Nvidia ที่ไม่ได้เก่าเกินไปตรงนี้เข้าไป จะช่วยให้ Generate เร็วขึ้นพอสมควรเลยครับ
                  set COMMANDLINE_ARGS=--xformers --autolaunch

                  หลังจากลงโปรแกรมเสร็จ ให้เรารันไฟล์ web user-ui.bat แล้วถ้ามันเข้าหน้าเว็บเลยก็ ok แต่ถ้ามันขึ้นจอดำๆ อย่างเดียว ก็ให้ กดปุ่ม Ctrl ค้าง แล้วคลิ๊กซ้ายไปที่ URL http://127.0.0.1:7860 ได้เลย มันจะเปิด Stable Diffusion Web UI ขึ้นมา ซึ่งนี่แหละคือหน้าจอโปรแกรมที่เราจะใช้

                  ทีนี้เพื่อเป็นการฉลองการติดตั้งโปรแกรม เราจะลอง Generate รูปขึ้นมาซักอัน (โดยไม่ต้องแก้ค่า Setting อะไรทั้งสิ้น) ด้วยคำว่า

                  Siamese cat waling on a beach

                  แล้วกัน โดยพิมพ์เข้าไปใน Image Prompt ในส่วนของ Tab ที่เขียนว่า txt2img แล้วกดปุ่ม Generate ปุ่มส้มๆ ที่ด้านขวาเลย

                  เท่านี้มันก็จะสร้่างรูปแมวไทยเดินบนชายหาดขึ้นมา ถ้าขึ้นรูปมาแสดงว่าสำเร็จแล้ว (รูปของคุณอาจไม่เหมือนของผม เพราะมันยังมีการใช้ Seed แบบ Random อยู่)

                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 437

                  ถ้าใครทำให้รูปขึ้นมาได้ถือว่าสำเร็จ! ยินดีด้วย ตอนนี้คุณลง Stable Diffusion ในเครื่องตัวเองได้แล้วล่ะ

                  คราวนี้เราลองมาให้มัน Generate รูปผู้หญิงไทยสวยๆ กัน ลองใส่ Prompt ว่า

                  Beautiful Thai woman sitting in coffee shop

                  เราจะพบว่าผลลัพธ์ที่ออกมา ก็เป็นรูปผู้หญิงไทยนั่งใน Coffee Shop นั่นแหละ แต่มันดูไม่เห็นสวยเลยซักนิด รูปดูไม่มีคุณภาพเลย (ถ้าเทียบกับ MidJourey นะ จะสวยกว่ามากๆ)

                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 438
                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 439
                  ทำไมออกมาธรรมดามากๆ

                  ป.ล. รูปทุกอันที่ Gen ออกมาจะอยู่ใน Folder Outputs โดยอันโนมัตินะครับ

                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 440

                  ทำยังไงรูปถึงจะสวย?

                  การที่รูปจะสวยหรือไม่นั้น ขึ้นอยู่กับหลายๆ อย่างพอสมควร มันไม่ใช่แค่ว่ามี Prompt ที่ดีแล้วรูปจะสวยได้ตามต้องการนะครับ

                  ถ้าให้ผม List ปัจจัยที่จะทำให้รูปสวยออกมาก็จะมีดังนี้

                  • ตัว Model หลักที่ใช้ : ตอนนี้เราใช้ Model มาตรฐาน คือ sd v1.4 ปกติเลย
                  • ตัว Model เสริม เช่น Lora, Textual Inversion (ซึ่งตอนนี้เราไม่ได้ใช้เลย )
                  • Text Prompt ที่ใส่เข้าไป : เป็นคำบรรยายว่าเราต้องการรูปอะไร เข่น ต้องอธิบายตัวละคร หรือ object ที่เราต้องการ ฉากหลัง แสง เทคนิคด้านศิลป์ต่างๆ มากมาย
                  • Negative Prompt ที่ใส่เข้าไป : ว่าเราไม่ต้องการอะไรในรูป
                  • Setting ต่างๆ ในหน้าจอ : ซึ่งเยอะมากๆ ตัวหลักๆ ที่มีผลมาก คือ Resolution ของรูป, Sampling method, Sampling steps, CFG Scales เป็นต้น
                  • ส่วนเสริม Extension ต่างๆ เช่น ที่ดังมากๆ คือ ControlNet ที่เอาไว้ควบคุมท่าทาง รวมถึงพวก x/y/z plot ที่เอาไว้ทดสอบค่าต่างๆ ได้ว่าตั้งค่าเท่านี้ๆ ผลจะออกมายังไง
                  • โชค : ยังไงหลักการขอการ Gen รูป มันมีปัจจัยเรื่องการ Random อยู่ด้วย ยังไงต้องมีโชคบ้างนิดหน่อย (ถ้ามีความรู้มาก ก็ไม่จำเป็นต้องใช้โชคเยอะ)

                  นอกจากนี้ยังมีเทคนิคการสั่ง AI อีกมากมาย เช่น นอกจากจะใช้ txt2img (text to image) แล้ว ยังมี img2img (image to image) ที่รองรับการ inpaint หรือให้ AI แก้เฉพาะจุดได้

                  ทั้งหมดนี้ ผมจะสอนในบทความถัดไปนะครับ สำหรับตอนนี้ ผมจะ Generate ให้ดูว่า ถ้าเราตั้งค่าได้อย่างถูกต้อง (และมี Model หลักและเสริมครบถ้วน) การจะได้รูปผู้หญิงนั่งที่ร้านกาแฟจะออกมาเป็นยังไง?

                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 441
                  วิธีใช้งาน AI สร้างรูปสุดเจ๋งและฟรีด้วย Stable Diffusion ฉบับมือใหม่ [ตอนที่1] 442
                  นี่ครับ แม้จะยังไม่ Perfect แต่สวยขึ้นเยอะ

                  ตอนต่อไป

                  ดังนั้นใครอยากรู้วิธีเขียน Prompt, Model เจ๋งๆ รวมถึงวิธีตั้งค่าแต่ละอันโดยละเอียด ก็โปรดติดตามตอนต่อไปได้เลย รับรองสนุกแน่ครับ!

                  รวมบทความ Stable Diffusion

                  • 10 ไอเดีย เรียนรู้ Excel ผ่าน ChatGPT AI สุดเจ๋ง

                    10 ไอเดีย เรียนรู้ Excel ผ่าน ChatGPT AI สุดเจ๋ง

                    ในบทความนี้ผมจะมาแนะนำ 10 ไอเดียเด็ด วิธีใช้ ChatGPT ช่วยให้เราเก่ง Excel โดยใช้เจ้า AI ที่ดังสุดๆ ในตอนนี้อย่าง ChatGPT มาช่วย พร้อมตัวอย่างของแต่ละไอเดียครับ

                    ChatGPT คืออะไร?

                    สำหรับคนที่ยังไม่รู้จักนะ ChatGPT เป็น AI (Artificial Intelligence) ด้านภาษาขั้นสูงที่พัฒนาโดย OpenAI เป็นเครื่องมืออัน ทรงพลังสำหรับการ Chat ด้วยลักษะของข้อความ ซึ่งสามารถตอบคำถามและให้คำแนะนำได้หลากหลาย

                    ChatGPT ได้รับการฝึกฝนเกี่ยวกับคลังข้อมูลข้อความจำนวนมหาศาลจากอินเทอร์เน็ต ทำให้สามารถทำความเข้าใจและสร้างข้อความในรูปแบบต่างๆ มากมาย

                    ข้อดีอย่างหนึ่งของ ChatGPT คือความสามารถในการให้ข้อมูลและคำแนะนำที่เป็นประโยชน์ในหัวข้อต่างๆ รวมถึง Microsoft Excel ด้วย

                    เมื่อใช้ ChatGPT เพื่อเรียนรู้ Excel เราสามารถจะได้รับการช่วยเหลือแบบเรียลไทม์ ฝึกฝนกับข้อมูลตัวอย่าง และเรียนรู้ผ่านโครงการและกิจกรรมที่ต้องลงมือปฏิบัติจริงได้ด้วย

                    โดยรวมแล้ว ChatGPT เป็นเครื่องมืออเนกประสงค์และมีค่าสำหรับทุกคนที่ต้องการพัฒนาทักษะ Excel และเพิ่มพูนความรู้ด้านซอฟต์แวร์ ไม่ว่าคุณจะเป็นผู้เริ่มต้นหรือผู้ใช้ขั้นสูง ChatGPT สามารถช่วยให้คุณเรียนรู้และเชี่ยวชาญ Excel ได้อย่างสนุกสนานมากขึ้นกว่าเดิม

                    วิธีการใช้งาน ChatGPT

                    ต่อไปนี้เป็นขั้นตอนในการใช้แพลตฟอร์มบนเว็บ OpenAI เพื่อคุยกับ ChatGPT

                    1. ไปที่เว็บไซต์ OpenAI ChatGPT: https://chat.openai.com/
                    2. เข้าสู่ระบบ หรือ สมัครสมาชิกบัญชี ซึ่งสามารถใช้ Google ACcount หรือ Microsoft Account ก็ได้
                    3. เริ่มใช้ ChatGPT ได้โดยพิมพ์ Chat หรือที่เรียกว่า “พรอมต์” ซึ่งก็คือประโยคหรือคำถามที่คุณส่งไปยัง ChatGPT เพื่อเริ่มการสนทนา
                      • ป.ล. แม้จะพิมพ์ไทยได้ แต่แนะนำให้ใช้ภาษาอังกฤษ จะ Work กว่ามาก (ใครไม่ถนัด eng ควรหัดนะ แต่ถ้าไม่ได้จริงๆ ก็ Google Translate ได้ครับ)
                    4. อ่านผลการตอบกลับ : ChatGPT จะทำการตอบกลับตามข้อความแบบเดียวกับการคุยกับมนุษย์เลย คุณสามารถอ่านคำตอบและใช้ข้อมูลเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับ Excel หรือรับความช่วยเหลือเกี่ยวกับปัญหาเฉพาะได้
                    5. ทำซ้ำตามต้องการ: คุณสามารถพิมพ์พร้อมต์ใน ChatGPT ต่อไปเพื่อรับข้อมูลเพิ่มเติมและคำแนะนำในขณะที่คุณทำงานผ่านเส้นทางการเรียนรู้ของ Excel

                    แค่นั้นแหละ! ด้วยขั้นตอนง่ายๆ เหล่านี้ คุณจะสามารถเริ่มใช้แพลตฟอร์มบนเว็บของ OpenAI เพื่อโต้ตอบกับ ChatGPT และรับความช่วยเหลือเกี่ยวกับการศึกษา Excel ของคุณได้แล้ว

                    ตัวอย่าง

                    คือมันจะเป็น Chat ถามตอบ คิดซะว่าคุยกับเพื่อนชาวต่างประเทศคนนึงที่รอบรู้เกือบทุกเรื่องละกัน (แต่เพื่อนคนนี้อาจตอบผิดบ้างนะ อยา่เชื่อ 100% ต้องทดสอบด้วย)

                    10 ไอเดีย เรียนรู้ Excel ผ่าน ChatGPT AI สุดเจ๋ง 443

                    ความเจ๋งคือ เราสามารถคุยโต้ตอบกับมันได้ และมันสามารถจำเรื่องที่คุยไปก่อนหน้าได้ ดังนั้นมันจึง flexible มากๆ เลย

                    เอาล่ะ เรามาดูตัวอย่าง การใช้ ChatGPT ในการเรียนรู้ Excelกันดีกว่า ว่ามีอะไรน่าสนใจบ้าง

                    Idea การใช้ ChatGPT


                    ช่วยแนะนำเกี่ยวกับสูตรและเครื่องมือต่างๆ

                    แนวทาง

                    เราสามารถถามวิธีทำสิ่งที่ต้องการไปได้เลย โดยที่ไม่จำเป็นต้องรู้ว่าจะใช้ฟังก์ชันอะไรด้วยซ้ำ ที่สำคัญคือ เราต้องลองทำจริงๆ เพราะบางครั้ง ChatGPT ก็มั่ว 555 (โดยเฉพาะพวกคีย์ลัดต่างๆ เนี่ยตัวตอบมั่วเลย)

                    10 ไอเดีย เรียนรู้ Excel ผ่าน ChatGPT AI สุดเจ๋ง 444

                    คำถาม

                    what formula to get text after space in excel

                    คำตอบ

                    If you want to extract the text after a space in Excel, you can use the RIGHT or MID functions. Here’s how you can use each function:

                    1. RIGHT function: The RIGHT function returns the specified number of characters from the end of a text string. To extract the text after the first space, you can use the following formula:
                    =RIGHT(A1,LEN(A1)-FIND(" ",A1))

                    Where:

                    • A1 is the cell containing the text string.
                    • LEN(A1) returns the length of the text string.
                    • FIND(” “,A1) returns the position of the first space in the text string.
                    1. MID function: The MID function returns a specific number of characters from a text string, starting at a specified position. To extract the text after the first space, you can use the following formula:
                    =MID(A1,FIND(" ",A1)+1,LEN(A1))

                    Where:

                    • A1 is the cell containing the text string.
                    • FIND(” “,A1) returns the position of the first space in the text string.
                    • LEN(A1) returns the length of the text string.

                    These formulas will return the text after the first space in the text string. If you need to extract text after more than one space, you’ll need to nest these functions to perform multiple searches and extractions.

                    I hope this helps! Let me know if you have any further questions about extracting text in Excel.

                    ให้มันช่วยแก้สูตรที่เขียนผิดให้

                    แนวทาง

                    บางทีเราเขียนสูตรแล้ว error หรือขี้เกียจเขียนให้ถูก syntax ก็ลองให้มันช่วยแก้ให้ได้เลย เจ๋งดีครับ

                    10 ไอเดีย เรียนรู้ Excel ผ่าน ChatGPT AI สุดเจ๋ง 445

                    คำถาม

                    can you help me fix formula =INDEX G3:G17 MATCH K3 E3:E17

                    คำตอบ

                    The formula you provided seems to have some errors. The correct formula should be:

                    =INDEX(G3:G17, MATCH(K3, E3:E17, 0))

                    The INDEX function retrieves a value in a specified range (G3:G17), based on the row number specified by the MATCH function. The MATCH function returns the relative position of a value (K3) within a specified range (E3:E17). The third argument of the MATCH function, 0, specifies an exact match.

                    Make sure to check the range and value references in your formula to ensure they are correct.

                    สร้างข้อมูลตัวอย่างใช้ฝึกฝนฝีมือ

                    แนวทาง

                    เราสามารถบอกให้มันทำข้อมูล Mockup แบบที่เราต้องการได้ เช่น มี heading อะไรบ้าง รายละเอียดเป็นยังไงก็ระบุได้หมด ถ้ายังทำไม่ตรงใจเรา ก็สามารถสั่งให้มันแก้ได้อีกจนกว่าเราจะพอใจ

                    คำถาม

                    please help create 10 rows mockup data about online shopping transaction as a table format

                    คำตอบ

                    Here is a mockup data of 10 rows of online shopping transactions in table format:

                    DateItemPrice
                    1/1/2023Shoes$100
                    1/2/2023Shirts$50
                    1/3/2023Bags$75
                    1/4/2023Sunglasses$25
                    1/5/2023Books$20
                    1/6/2023Gadgets$150
                    1/7/2023Jackets$80
                    1/8/2023Headphones$35
                    1/9/2023Watches$200
                    1/10/2023Backpacks$60

                    i want some duplicated date and item. and also need qty column

                    Here is a mockup data of 10 rows of online shopping transactions in table format with duplicated dates and items and a quantity column:

                    DateItemPriceQty
                    1/1/2023Shoes$1002
                    1/2/2023Shirts$503
                    1/3/2023Bags$751
                    1/4/2023Sunglasses$255
                    1/4/2023Sunglasses$252
                    1/5/2023Books$201
                    1/6/2023Gadgets$1502
                    1/7/2023Jackets$801
                    1/8/2023Headphones$353
                    1/9/2023Watches$2001

                    ok, please add non dup transaction id

                    Sure, here’s the mockup data of 10 rows of online shopping transactions in table format with duplicated dates and items, a quantity column, and a non-duplicated transaction ID:

                    Transaction IDDateItemPriceQty
                    T00011/1/2023Shoes$1002
                    T00021/2/2023Shirts$503
                    T00031/3/2023Bags$751
                    T00041/4/2023Sunglasses$255
                    T00051/4/2023Sunglasses$252
                    T00061/5/2023Books$201
                    T00071/6/2023Gadgets$1502
                    T00081/7/2023Jackets$801
                    T00091/8/2023Headphones$353
                    T00101/9/2023Watches$2001

                    นี่ไง พอใช้ได้ตามที่เราต้องการแล้ว จากนั้นเราจะเอาข้อมูลนี้ไปฝึกอะไรก็ตามใจ

                    ทำงานอัตโนมัติด้วยโค้ด VBA

                    แนวทาง

                    เราสามารถถาม VBA Code จาก ChatGPT ได้เลย แค่บอกว่าต้องการอะไร

                    คำถาม

                    i want vba code to change tab color for all sheets to be yellow except sheet name “config”

                    คำตอบ

                    Here’s a VBA code that changes the tab color for all sheets in an Excel workbook to yellow, excluding the sheet named “Config”:

                    Sub ChangeTabColor()
                        Dim ws As Worksheet
                        For Each ws In ThisWorkbook.Sheets
                            If ws.Name <> "Config" Then
                                ws.Tab.Color = RGB(255, 255, 0)
                            End If
                        Next ws
                    End Sub
                    

                    To use this code, follow these steps:

                    1. Open the Excel workbook you want to modify.
                    2. Press “Alt + F11” to open the VBA editor.
                    3. In the editor, select “Insert” from the menu bar and then select “Module”.
                    4. Copy and paste the code into the new module.
                    5. Save and close the VBA editor.
                    6. Back in the Excel workbook, press “Alt + F8” to open the macro dialog box.
                    7. Select the “ChangeTabColor” macro and click “Run”.

                    This will change the tab color for all sheets in the workbook to yellow, except for the sheet named “Config”.

                    จำลองการสัมภาษณ์งาน และข้อสอบต่างๆ

                    แนวทาง

                    เราสามารถซ้อมตอบคำถาม Excel เวลาจะไปสัมภาษณ์งาน หรือทำข้อสอบเกี่ยวกับ Excel ก็ได้

                    คำถาม

                    please create Mock up job interview that test my excel knowledge in variety of excel abilities

                    คำตอบ

                    Sure, here is a list of mock job interview questions that could test a variety of Excel abilities:

                    1. Can you explain the difference between absolute and relative cell referencing?
                    2. How would you create a chart in Excel to visualize data?
                    3. Can you walk me through the steps to use the VLOOKUP function in Excel?
                    4. Can you explain how to use conditional formatting in Excel?
                    5. How would you set up a budget tracking spreadsheet in Excel?
                    6. Can you demonstrate how to use Pivot Tables in Excel?
                    7. Can you explain the difference between the IF and IFERROR functions in Excel?
                    8. How would you use the INDEX and MATCH functions together to extract data from a table in Excel?
                    9. Can you show how to create a drop-down list in Excel?
                    10. How would you automate a process in Excel using a macro?
                    11. Can you explain how to use the Solver tool in Excel?
                    12. Can you walk me through the steps to create a data validation rule in Excel?
                    13. How would you set up a loan amortization table in Excel?
                    14. Can you explain how to use the SUMIF and SUMIFS functions in Excel?
                    15. How would you use the Text to Columns feature in Excel to split text data into separate columns?

                    Note: You may tailor these questions to match your Excel knowledge level or the specific requirements of the job you’re interviewing for.

                    ฝึกฝน Excel ในรูปแบบเล่นเกมตอบคำถาม

                    แนวทาง

                    อันนี้คล้ายกับ Job Interview แต่ว่าเราอยากจะตอบเป็น Choice แล้วให้มันบอกว่าเราตอบถูกหรือผิด

                    คำถาม

                    please ask me choice-based quiz about excel knowledge. let me choose the choice then tell me if my answer is correct or not. one by one question, show my accum score after each question

                    คำตอบ

                    Sure, here is the first question:

                    Question 1: What is the purpose of the SUM function in Excel?

                    A) To find the average of a range of cells
                    B) To find the total of a range of cells
                    C) To find the minimum value in a range of cells
                    D) To find the maximum value in a range of cells

                    Please enter A, B, C, or D.

                    เรา :

                    B

                    ChatGPT :

                    Correct! The SUM function is used to find the total of a range of cells.

                    Your current score is 1.

                    Here is the next question:

                    Question 2: What is the shortcut key to apply the AutoSum formula in Excel?

                    A) Alt + =
                    B) Ctrl + =
                    C) Shift + =
                    D) Ctrl + Alt + =

                    Please enter A, B, C, or D.

                    เรา :

                    A

                    ChatGPT :

                    Correct! The shortcut key to apply the AutoSum formula in Excel is Alt + =.

                    Your current score is 2.

                    Here is the next question:

                    Question 3: How do you create a pivot table in Excel?

                    A) Select the data, go to the “Data” tab, click the “Sort & Filter” button, and choose “Sort Ascending”
                    B) Select the data, go to the “Data” tab, click the “PivotTable” button, and choose “PivotTable”
                    C) Select the data, go to the “Insert” tab, click the “Table” button, and choose “PivotTable”
                    D) Select the data, go to the “Home” tab, click the “Sort & Filter” button, and choose “Sort Descending”

                    Please enter A, B, C, or D.

                    แล้วเราก็เล่นแบบนี้ไปเรื่อยๆ ได้เลยครับ 555

                    แนวทางหรือ Best Pratice ในการแก้ปัญหา

                    คำถาม

                    what is correct way to design database before pivot

                    คำตอบ

                    Designing a database before creating a pivot table is an important step in ensuring that the pivot table is accurate and effective. The following are best practices for designing a database before using a pivot table:

                    1. Organize the data: Make sure that the data is organized in a clear and logical manner, with consistent and meaningful column and row headers.
                    2. Remove duplicates: Remove any duplicate records to avoid skewing the results of the pivot table.
                    3. Standardize data formats: Ensure that all data is formatted consistently and accurately, such as date and currency formats.
                    4. Use proper data types: Assign appropriate data types to each column to ensure that Excel can accurately manipulate the data.
                    5. Create meaningful labels: Create meaningful and descriptive labels for each column to help clarify the data.
                    6. Normalize the data: Normalize the data by breaking it down into smaller, more manageable tables, and establishing relationships between the tables.
                    7. Sort the data: Sort the data in a logical and meaningful manner to help Excel quickly and accurately manipulate the data.
                    8. Add calculated columns: Add calculated columns, if needed, to help provide additional insights into the data.

                    By following these best practices, you can ensure that your database is well-designed and optimized for use with pivot tables.

                    ไอเดียวิเคราะห์ข้อมูลเชิงลึกที่น่าสนใจ

                    คำถาม

                    what is interesting way to analyze hr employee data to get meaningful insights

                    คำตอบ

                    To analyze HR employee data and gain meaningful insights, here are some interesting approaches:

                    1. Demographic analysis: Analyze the age, gender, education, and other demographic data of your employees to understand the makeup of your workforce.
                    2. Turnover analysis: Track and analyze employee turnover rates to identify any trends or patterns and determine if there are any factors contributing to high turnover.
                    3. Performance analysis: Use data on employee performance, such as performance ratings, to identify any trends or patterns and determine which employees are outperforming their peers.
                    4. Skills analysis: Analyze the skills and experience of your employees to determine which skills are most important for your organization and where there may be skill gaps.
                    5. Engagement analysis: Measure and analyze employee engagement to determine the factors that contribute to high engagement levels and identify areas for improvement.
                    6. Compensation analysis: Analyze compensation data to determine if there are any disparities in pay based on gender, race, or other factors.

                    By using these approaches, you can gain a deeper understanding of your workforce and make data-driven decisions to improve HR processes and employee experiences.

                    ช่วย Brainstorm Idea แนะนำ Project ที่น่าทำ

                    คำถาม

                    i want to create excel project that not too difficult, what should i do, please give me 10 project idea

                    คำตอบ

                    Sure! Here are 10 simple Excel project ideas:

                    1. Personal Budget Tracker: Create a spreadsheet to track your income and expenses.
                    2. Meal Planner: Create a spreadsheet to plan and track your weekly meals, including grocery shopping list.
                    3. Monthly Bill Tracker: Create a spreadsheet to keep track of all your monthly bills, including due dates and payment status.
                    4. Investment Tracker: Create a spreadsheet to track your investment portfolio, including stock prices, returns, and dividends.
                    5. Time Tracker: Create a spreadsheet to track how you spend your time, including work, leisure, and sleep.
                    6. Exercise Tracker: Create a spreadsheet to track your exercise routine, including type of exercise, duration, and calorie burn.
                    7. Home Inventory: Create a spreadsheet to catalog your belongings, including make, model, purchase date, and value.
                    8. Weight Tracker: Create a spreadsheet to track your weight loss or gain, including diet, exercise, and other factors.
                    9. Book Tracker: Create a spreadsheet to track books you have read, including title, author, genre, and rating.
                    10. Project Planner: Create a spreadsheet to plan and track a personal or work project, including tasks, deadlines, and progress.

                    แหล่งเรียนรู้ Excel เพิ่มเติมในเรื่องที่สนใจ

                    คำถาม

                    who is top 5 experts in power query

                    คำตอบ

                    It’s difficult to determine the exact top 5 experts in Power Query as it can be subjective and based on various factors such as their contribution to the community, expertise, recognition, and online presence. However, some of the well-known names in the Power Query community are:

                    1. Ken Puls
                    2. Chris Webb
                    3. Matt Allington
                    4. Miguel Escobar
                    5. Avi Singh

                    ความเห็นเกี่ยวกับ AI

                    และนี่ก็เป็น 10 ไอเดียในการใช้ ChatGPT ช่วยทำให้เราเรียนรู้ Excel ได้ดีขึ้น จะเห็นว่าทักษะสำคัญมากๆ ตอนนี้ก็คือภาษาอังกฤษนะครับ เพราะมันจำเป็นสำหรับการสื่อสารกับ AI เป็นอย่างมาก

                    และอย่าลืมว่า เรื่อง AI มันจะมาแน่ๆ ในทุกมิติของการใช้โปรแกรมต่างๆในอนาคต และอีกไม่นาน Excel เองก็จะมี AI ที่เก่งมากๆ มาช่วยเราจัดการข้อมูลแน่นอน

                    จริงๆ ปัจจุบัน Excel ก็มี AI อยู่แล้วนะ เช่น Flash Fill, Column From Example หรือแม้แต่ ปุ่ม Analyze Data แต่เราอาจยังไม่ค่อยได้ใช้ เพราะมันยังไม่เก่งมาก แต่มันจะเก่งขึ้นแน่ๆ

                    ดังนั้น ในอนาคต นอกจากที่จะต้องวิเคราะห์ข้อมูลเก่งแล้ว เราอาจจะต้องมาฝึกฝนวิธีการสั่งงาน AI ให้ได้ดั่งใจด้วยเช่นกัน หึหึ

                    สำหรับประเด็นเรื่องที่ AI เก่งมากจนจะมาแทนที่งานของมนุษย์นั้น ผมเห็นว่าก็มีความเป็นไปได้สูงสำหรับงานที่ค่อนข้าง Routine ไม่ได้ใช้ความสามารถในการวิเคราะห์อะไรที่มันลึกซึ้งมากนัก แต่สำหรับคนที่มีฝีมืออยู่แล้ว การมี AI มาเนี่ยจะช่วยเพิ่มประสิทธิภาพให้คนนั้นได้มหาศาล ดังนั้นในตอนนี้เรายังพอมีเวลาฝึกฝนตัวเองให้มีความสามารถที่เฉียบคมขึ้นได้ จะได้สั่ง AI ได้ดีขึ้น ไม่ใช่ให้มันมาแทนที่เรา

                    ป.ล. รูปปกของบทความนี้ ผมก็ใช้ AI ชื่อ MidJourney สร้างขึ้นมาได้แบบสบายๆ ตอนนี้โลกมันไปไกลมาจริงๆ ครับ (ผมเคยเขียนบทความวิธีใช้แบบละเอียดไว้ด้วย แม้ตอนนี้จะมีตัว version ใหม่แล้ว แต่หลักการในบทความยังใช้ได้อยู่)

                    10 ไอเดีย เรียนรู้ Excel ผ่าน ChatGPT AI สุดเจ๋ง 446
                  • การทำ Simulation ด้วย Excel

                    การทำ Simulation ด้วย Excel

                    Simulation คืออะไร?

                    Simulation คือการใช้ Model หรือแบบจำลองที่ใช้ความสัมพันธ์ทางคณิตศาสตร์ มาช่วยคำนวณผลลัพธ์ของสิ่งที่เราสนใจ โดยที่หากเราสามารถสร้างแบบจำลองที่แม่นยำใกล้เคียงกับความเป็นจริง เราก็จะสามารถใช้แบบจำลองช่วยทำนายผลลัพธ์ที่อาจจะเกิดขึ้นได้ด้วย ซึ่งจะช่วยให้เราสามารถตัดสินใจได้ดียิ่งขึ้น

                    จริงๆ แล้วเราก็ใช้ Excel คำนวณเรื่องต่างๆ ตามปกติอยู่แล้ว เช่น คำนวณผลกำไรของธุรกิจ คำนวณพื้นที่ คำนวณปริมาตร แต่เรามักจะใช้ Input ของสูตรเป็นค่าที่มีความแน่นอนไปเลย เช่น

                    ผลกำไรของธุรกิจ = รายได้รวมรวม - ค่าใช้จ่ายรวม

                    หากคำนวณผลกำไรของธุรกิจที่เกิดขึ้นแล้ว ก็เอารายได้จริง ลบ ค่าใช้จ่ายจริง ไปเลย แบบนั้นก็ไม่ต้องมาทำ Simulation อะไรทั้งสิ้น

                    ผลกำไรของธุรกิจ (แน่นอน) =  
                    รายได้รวมรวม (แน่นอน) - ค่าใช้จ่ายรวม (แน่นอน)
                    
                    //แบบนี้คำนวณตรงๆ ไปเลยไม่ต้องใช้ Simulation

                    แต่ถ้าเราจะคำนวณผลกำไร ที่อาจจะเกิดขึ้นในอนาคต ซึ่งเป็นเรื่องที่ไม่แน่นอน แบบนี้เราสามารถใช้การทำ Simulation เพื่อจำลองว่า ยอดขายหรือต้นทุนที่มีโอกาสเกิดขึ้นได้หลายค่านั้นจะออกมาเป็นเช่นไร

                    ผลกำไรของธุรกิจ (ไม่แน่นอน) =  
                    รายได้รวมรวม - ค่าใช้จ่ายรวม 
                    (อย่างน้อยตัวใดตัวหนึ่งไม่แน่นอน)
                    
                    //แบบนี้ใช้ Simulation เพื่อจำลองความไม่แน่นอน

                    เรื่องการแจกแจงความน่าจะเป็นเป็นความรู้ทางสถิติแบบนึง ใครไม่คุ้นสามารถเริ่มอ่านเรื่องสถิติได้ที่นี่

                    ทำไมต้องทำ Simulation?

                    เราใช้ Simulation เพื่อจำลองผลลัพธ์ที่ไม่แน่นอนนั้นๆ ออกมาให้เราได้เข้าใจ และเห็นภาพชัดขึ้นว่าจะเกิดอะไรขึ้นได้บ้าง ซึ่งดีกว่าการใช้ค่ากลางอย่างเช่นค่าเฉลี่ยมาคำนวณเพียงอย่างเดียว โดยที่เราไม่ต้องทำการทดสอบจริงๆ ซึ่งจะม่ีค่าใช้จ่ายสูง

                    พอทำ Simulation แล้ว เราจะเห็นว่าขอบเขตของค่าผลลัพธ์เป็นเช่นไร? ค่าที่คาดหวังโดยเฉลี่ยเป็นเท่าไหร่? เป็นต้น ทั้งหมดเพื่อช่วยให้เราสามารถเลือกทางเลือกหรือตัดสินใจได้ดียิ่งขึ้น ซึ่งจะช่วยให้ประหยัดค่าใช้จ่ายและสร้างผลกำไรทางธุรกิจได้ในที่สุด

                    Excel ใช้ทำ Simulation ยังไง?

                    การที่จะสร้างความไม่แน่นอนขึ้นมาได้นั้น เราจะใช้การสุ่มค่ามาช่วย ซึ่งใน Excel มีฟังก์ชันที่ใช้สุ่มตัวเลขอยู่หลายฟังก์ชัน เช่น

                    • RAND() ซึ่งเป็นการสุ่มเลขตั้งแต่ 0-1 โดยเป็นทศนิยมได้
                    • RANDBETWEEN(bottom,top) โดยเป็นการสุ่มเลขจำนวนเต็มตั้งแต่ botton ถึง top
                    • RANDARRAY(rows,columns,min,max,integer)

                    ตัวหลักๆ ที่เราจะใช้คือ RAND() เพราะได้ค่าทศนิยมละเอียด และใช้ได้ใน Excel ทุก version โดยที่เมื่อ RAND แล้วจะได้เลขโดยสุ่มเลขตั้งแต่ 0-1 ออกมาค่านึง

                    การทำ Simulation ด้วย Excel 447

                    แล้วพอมีการคำนวณใหม่ (เช่น กด F9) ก็จะได้ค่าใหม่ออกมาเป็นอีกค่าหนึ่ง ซึ่งนี่ก็คือ Concept ของการสุ่มใน Excel นั่นเอง

                    การทำ Simulation ด้วย Excel 448

                    หากเราเอาค่าที่สุ่มได้ตั้งแต่ 0-1 นั้น ออกมาใช้กับ Distribution รูปแบบต่างๆ ได้ เช่น Uniform Distribution, Normal Distribution, T-Distribution, Binomial Distribution, Poisson Distribution และอีกมากมาย

                    ตัวอย่างการสุ่มแบบง่าย

                    ถ้าเป็นการสุ่มแบบ Yes, No ที่มีโอกาสเกิด xx% เช่น โอกาสที่ทำงานสำเร็จ คือ 70% แบบนี้เราก็แค่ใช้ RAND เทียบกับค่า 0.7 ได้ เช่น

                    =IF(RAND()<=0.7,"สำเร็จ","ล้มเหลว")

                    ตัวอย่างการสุ่มแบบ Distribution ซับซ้อน

                    แต่ถ้าโอกาสที่จะเกิดขึ้นมันมีความน่าจะเป็นแบบ Distribution ที่ซับซ้อนขึ้นล่ะ??

                    วิธีที่ง่ายที่สุดอันนึงคือการใช้ Inverse ของความน่าจะเป็น Distribution เหล่านั้นในการหาค่าที่สนใจ

                    ตัวอย่างง่ายๆ เช่น กรณี Normal Distribution เราจะใช้ NORM.DIST กับ NORM.INV จะเป็นการคำนวณกลับกัน

                    การทำ Simulation ด้วย Excel 449

                    Key สำคัญคือเราจะ Random ความน่าจะเป็นในการเกิดเหตุการณ์นั้นๆ ออกมา แล้วใช้ Inverse เพื่อหาค่าที่สนใจ ยกตัวอย่างเช่น

                    สมมติว่า Demand ของสินค้าที่เราจะขาย มีการแจกแจกแบบ Normal Distribution ที่มีค่าเฉลี่ยคือ 200 ชิ้นต่อเดือน และมีค่าเบี่ยงเบนมาตรฐานที่ 50 ชิ้นต่อเดือน ถ้าลองคำนวณยอดขายตลอด 1 ปีอาจจะได้คำตอบแบบนี้ (แน่นอนว่าถ้ากด F9 เลขก็จะเปลี่ยนอีก)

                    การทำ Simulation ด้วย Excel 450

                    มันจะช่วยให้เราเห็นภาพได้ว่า โดยเฉลี่ยแล้วยอดอาจจะอยู่ประมาณ 200 จริงๆ แต่บางเดือนก็อาจจะน้อย บางเดือนก็เยอะ เป็นต้น

                    นอกจากนี้ เรายังสามารถใช้ Data Table มาช่วย Run Simulation หลายๆ รอบได้ตามต้องการ (Simulate เป็นพันครั้งเลยก็ได้ ยิ่งเยอะยิ่งเห็นความเป็นไปได้ชัดเจนมากขึ้น)

                    ถ้าอยากลองสร้างตารางแจกแจงความถี่ของยอดขายที่ได้ ก็สามารถใช้กราฟแบบ Histogram หรือว่าจะใช้ Analysis Toolpak โดยใช้การวิเคราะห์แบบ Histogram อีกทีก็ได้ครับ

                    การทำ Simulation ด้วย Excel 451

                    นี่แค่เริ่มต้นเท่านั้น

                    ซึ่งอันนี้เป็นตัวอย่างง่ายๆ ในการใช้ความรู้ทางสถิติ มาผสมกับเครื่องมือใน Excel เพื่อสร้าง Simulation ยอดขายขึ้นมาเท่านั้น

                    ในชีวิตจริง มันอาจจะมี Simulation หลายๆ เรื่อง ผูกโยงกันจนกลายเป็น Simulation Model ที่ซับซ้อนขึ้น และตรงกับความเป็นจริงมากขึ้นในที่สุด

                    ซึ่งเร็วๆ นี้ผมจะมีการทำคอร์สออนไลน์ใหม่เรื่อง Excel Solver & Simulation ขึ้นมา เพื่อให้ผู้ที่อยากจะใช้ Excel ในงานวางแผนต่างๆ สามารถใช้ Excel ช่วยวางแผนได้อย่างมีประสิทธิภาพมากที่สุด ใครที่สนใจเนื้อหาเรื่องพวกนี้ บอกเลยว่า พลาดไม่ได้เลยครับ!!

                  • แกะเคล็ดวิชา Excel Wizard ในการแข่ง Speed Run Excel ระดับโลก

                    แกะเคล็ดวิชา Excel Wizard ในการแข่ง Speed Run Excel ระดับโลก

                    เมื่อ 29 กย. 65 ที่ผ่านมานี้ มีข่าวดังในวงการ Excel ประเทศไทย นั่นคือคุณ Bo Rydobon (นามแฝง) หรือ ฉายา Excel Wizard ได้มีการแข่ง Excel Speed Run กับ Laurence Lau ผู้ที่ได้ Rank คะแนนรวมอันดับ 1 ในการแข่งขัน FMWC ปี 2022 ณ ตอนนี้ (ดูคะแนนล่าสุดได้ที่นี่)

                    ซึ่งการแข่ง Speed Run เป็นการแข่งที่เน้นความเร็ว โดยมันเป็นเคสที่ผู้แข่งรู้โจทย์อยู่แล้ว และสามารถเตรียมตัวก่อนมาแข่งได้ครับ ซึ่งยิ่งน่าสนใจเลยว่าแต่ละคนจะงัดเทคนิคอะไรมาใช้แก้โจทย์ให้ได้เร็วที่สุด (รู้โจทย์แต่ห้ามพิมพ์การแก้โจทย์ไว้ก่อนนะ ไม่งั้นมันวัดอะไรไม่ได้เลย 55)

                    ปรากฏว่าทั้งคู่แข่งขันอย่างดุเดือดมากๆ แต่ละคนใส่เทคนิคเจ๋งๆ กันไม่ยั้ง แต่สุดท้ายคุณ Bo ก็ชนะไปอย่างงดงาม 3 Stage รวด! ทั้งๆ ที่คุณ Laurence Lau นี่คือเก่งมากๆ นะ

                    ซึ่งคลิปการแข่งขันคืออันนี้ครับ ผมแนะนำให้ลองดูอย่างน้อยซักแป๊ปนึงจะได้เข้าใจอารมณ์การแข่งว่ามันสุดยอดขนาดไหน

                    ซึ่งหลายคนที่ดูคลิปการแข่งขันนี้ไปแล้วคงรู้สึกคล้ายๆ กัน ประมาณนี้คือ

                    • “มันน่าทึ่งมากๆ คนไทยสุดยอด!”
                    • “Excel ทำได้ขนาดนี้เลยเหรอ?”
                    • “ใช้ Excel โคตรไว ทำได้ไง!?”
                    • “ทำอะไรกันเนี่ย ดูไม่รู้เรื่องเลย! แต่ดูเจ๋งดีนะ 55”

                    ดังนั้นในบทความนี้ผมจึงขออาสามาแกะเคล็ดลับหรือเทคนิคเจ๋งๆ ที่คุณโบใช้ในการแข่ง เผื่อเป็นความรู้ให้พวกเราได้เอาไปปรับใช้ในการทำงานของตัวเองด้วยครับ (บทความนี้ขออนุญาตคุณโบแล้วครับ)

                    แนวทางทั่วไปในการเพิ่มความเร็วการทำงาน

                    • วางแผนหน้าตา Layout ของ Model ให้ทำงานได้ง่าย
                    • ทำลาย Conditional Format ที่ไม่จำเป็นทิ้งซะ จะทำให้ทำงานเร็วขึ้น
                    • ใช้ Keyboard Shortcut (Hot Keys) แทนการใช้ Mouse
                    • แปลงสูตรที่ไม่ต้องการใช้อีกแล้วให้กลายเป็น Value
                    • ใช้ Defined Name เพื่อกำหนดพื้นที่การเลือกข้อมูลให้สะดวกขึ้น
                    • ใช้ Data Table เท่าที่จำเป็น ถ้าเลี่ยงใช้สูตรธรรมดาได้ก็ใช้สูตร
                    • ใช้ Excel365 เพื่อให้รองรับฟังก์ชันเยอะที่สุดรวมถึง Dynamic Array ที่เราสามารถเขียนสูตรช่องเดียวแล้วผลลัพธ์สามารถ Spill ล้นช่องนั้นออกมาได้เลย

                    ซึ่งเดี๋ยวเรามาดูรายละเอียดกันครับ ว่าคุณโบใช้หลักการข้างบนนี้แบบไหนบ้าง?

                    ลงรายละเอียดแต่ละเทคนิค

                    ถ้าหากเพื่อนๆ ได้ดูคลิปแค่ไม่กี่วินาทีแรกที่คุณโบเริ่มทำโจทย์ ก็จะพบว่ามีการใช้ Shortcut มากมายจนน่าทึ่ง! เช่น

                    แกะเคล็ดวิชา Excel Wizard ในการแข่ง Speed Run Excel ระดับโลก 452

                    Ctrl+A เลือกทั้งหมดในพื้นที่ -> Ctrl+C เพื่อ copy -> Ctrl+Pg Dn ไป Sheet ถัดไป -> Ctrl+ลูกศร เพื่อกระโดดไปทิศทางนั้นๆ จนสุด -> Ctrl+V เพื่อ Paste

                    ทำลาย Conditional Format ทั้งชีท โดยกด Alt, h, l, c, e -> เข้าไปตั้งชื่อ Defined Name โดยเลือก Range ที่ต้องการ แล้วกด Alt+F3 พิมพ์ชื่อแล้วกด Enter

                    แกะเคล็ดวิชา Excel Wizard ในการแข่ง Speed Run Excel ระดับโลก 453

                    แค่ดูก็เหนื่อยแล้วว ผมก็เลยขอเอาเทคนิคที่เกิดขึ้นมาจัดหมวดหมู่ให้เราเข้าใจได้ง่ายขึ้นดังนี้ครับ

                    วางแผนการเตรียมพื้นที่ตารางให้ทำงานง่ายขึ้น

                    • เนื่องจากการแข่งนี้ผู้แข่งรู้โจทย์ล่วงหน้าแล้ว ดังนั้นการหาวิธีการทำงานเพื่อให้ทำงานได้เร็วที่สุดจึงสำคัญมากๆ ซึ่งคุณโบวางแผนเรื่องนี้มาเป็นอย่างดี
                    • คุณโบมีการจัดเตรียมหน้าตาตารางหลายๆ อย่างให้ทำงานสะดวกขึ้น เช่น Copy ตาราง Assumption จากหน้าคำตอบมาหน้าแผนที่ด้วย
                    • มีการกำหนดทิศทางของรถที่วิ่งว่าเป็น ขึ้น ลง ซ้าย ขวา ด้วย U,D,L,R เพื่อให้สามารถเขียนสูตรเพื่อคำนวณระยะทางน้อยสุดในการที่รถในเคสจะวิ่งจากจุดนึงไปยังอีกจุดนึงตามกฎจราจรในเคสได้สะดวกขึ้น
                      • กฏจราจรคือ วิ่งชิดขวาเท่านั้น และการกลับรถจะกลับได้ที่จุดตัดถนนเท่านั้นเป็นต้น ซึ่งถ้าไม่ Model ให้ดีจะคำนวณระยะวิ่งผิดพลาดหมดเลย
                    แกะเคล็ดวิชา Excel Wizard ในการแข่ง Speed Run Excel ระดับโลก 454
                    • มีการสร้างแผนที่เพื่อคำนวณเรื่องต่างๆ แยกออกมาจากแผนที่หลัก เช่น
                      • แผนที่แสดง Address Cell Reference ที่แสดงพิกัดของแต่ละช่องของแผนที่หลัก
                      • แผนที่คำนวณระยะทางจากจุดที่กำหนดไปยังเป้าหมาย และเตรียม Reuse แผนที่นี้อีกครั้งในโจทย์ที่ต้องคำนวณระยะเวลาการเดินทางได้อย่างชาญฉลาด
                    แกะเคล็ดวิชา Excel Wizard ในการแข่ง Speed Run Excel ระดับโลก 455
                    ช่วงแรกของการแข่งขันมีการเตรียมพื้นที่และตั้งชื่อไว้อย่างดี
                    • สำหรับ Stage ที่ห้ามใช้ Mouse คุณโบได้ไล่ทำการตั้งชื่อ Range ที่สำคัญไว้ทั้งหมด แล้วเขียนสูตรโดยอ้างถึงชื่อเหล่านั้น ทำให้ไม่ต้องย้ายตำแหน่งไปมาเลย

                    เทคนิคในการเลือกข้อมูล

                    • ใช้ปุ่ม Enter กับ Tab อย่างเหมาะสม เช่น ถ้าจะให้ใส่ข้อมูลแล้วลงล่างก็ Enter ถ้าจะใส่แล้วไปขวาก็กด Tab เอา
                    • Ctrl+A ช่วยเลือกทั้งหมดในพื้นที่ที่ต่อเนื่องกัน
                    • ใช้ปุ่มลูกศรบน Keyboard เพื่อเลือกข้อมูลที่ไม่ไกลมาก
                    • Ctrl+ลูกศร เพื่อกระโดดไปทิศทางนั้นๆ จนสุด
                    • Ctrl+Shift+ลูกศร เพื่อเลือกพื้นที่แบบกระโดดไปทิศทางนั้นๆ
                    • Ctrl+PgDn ไป Sheet ถัดไป
                    • เข้าไปตั้งชื่อ Defined Name โดยเลือก Range ที่ต้องการ -> แล้วกด Alt+F3 พิมพ์ชื่อ -> กด Enter
                    • กด F5 เพื่อ Go to Name ที่ตั้งไว้
                    • ถ้าเป็นพื้นที่แปลกๆ เช่น พื้นที่ถนน คุณโบใช้วิธีการเลือกด้วยการ Find All แบบหา Cell สีเทา -> แล้ว Ctrl+A ในเมนู Find
                    • หรืออาจใช้การเลือกพื้นที่เฉพาะที่มีสูตร โดยใช้ Go to Special -> Formula

                    เทคนิคการ Copy Paste

                    • Ctrl+C เพื่อ copy -> Ctrl+V เพื่อ Paste อันนี้ Basic ๆ แต่ที่เจ๋งกว่าคืออันถัดไป
                    • เลือกข้อมูล => กด Ctrl+D เพื่อ Fill Down ลงมาข้างล่าง (D=Down) (ซึ่งเร็วกว่ากด Copy -> Paste)
                      • ไม่งั้นการเลือก Range จะยากขึ้นอีกหน่อยเพราะบางทีต้องกด Ctrl + . เพื่อเปลี่ยนจุดยึดของ Range อีกทีกลายเป็น Ctrl + . Shift → Ctrl+D
                      • ป.ล. สามารถกด Ctrl+R เพื่อ Fill Right ไปด้านขวา (R=Right)
                    • หลังจากเลือกพื้นที่ที่ต้องการ -> F2 เพื่อ Edit สูตร -> Ctrl+Enter เพื่อใส่สูตรทั้งพื้นที่พร้อมกันได้เลย แบบไม่ต้อง Copy Paste หรือ Fill ก็ได้

                    เทคนิคการเขียนสูตร

                    • ใช้สูตร Dynamic Array ช่วยให้การทำงานสะดวกรวดเร็วขึ้นมาก เช่น ใช้ Array 2 แกนเชื่อมกันแบบไขว้เพื่อสร้างตาราง Address
                    • ใช้การพิมพ์ชื่อฟังก์ชันบางส่วนแล้วกด Tab เพื่อให้ Auto Complete แล้วเปิดวงเล็บให้อัตโนมัติ แทนที่จะนั่งพิมพ์เองทุกตัว ( เหมือนว่า Laurence Lau จะถนัดพิมพ์เอง)
                    • มีการใช้ฟังก์ชันที่เหมาะสมกับการแก้ปัญหา เช่น แทนที่จะใช้ IF ซ้อนกันหลายอัน คุณโบก็เลือกจะใช้ฟังก์ชัน SWITCH ที่เขียนง่ายและเข้าใจง่ายกว่าด้วย

                    เช่น แทนที่จะเขียน IF ซ้อนกันว่า

                    =IF(ทิศทางถนนแนวตั้ง="U",ระยะทางช่องบน,
                    IF(ทิศทางถนนแนวตั้ง="D",ระยะทางช่องบน,999))

                    คุณโบใช้ SWITCH แทนแบบนี้ได้ ซึ่งดูคลีนกว่า

                    =SWITCH(ทิศทางถนนแนวตั้ง,"U",ระยะทางช่องบน,"D",ระยะทางช่องล่าง,999)

                    พอใช้สูตรประกอบกันทั้ง IF + MIN + SWITCH + SUMIFS ได้อย่างลงตัวด้วย Logic ที่เยี่ยมยอด ก็สามารถคำนวณระยะเวลาจากจุดเริ่มต้นที่กำหนดไปยังตำแหน่งต่างๆ ได้อย่างสวยงาม (ต้องวิ่งตามกฎจราจรด้วยนะ!!)

                    ซึ่งมีการใช้สูตรประมาณนี้

                    =
                    IF(จุดStart=ตำแหน่งถนนนั้นๆ,1,
                    MIN(
                    SWITCH(ทิศทางถนนแนวตั้ง,"U",ระยะทางช่องบน,"D",ระยะทางช่องล่าง,999),
                    SWITCH(ทิศทางถนนแนวนอน,"R",ระยะทางช่องขวา,"L",ระยะทางช่องซ้าย,999)
                    )
                    +1)
                    • ซึ่งจะเห็นว่าสูตรนี้มีการอ้างอิงค่าจากช่องบนล่างซ้ายขวาของตัวเองด้วย ยกเว้นจุดเดียวคือจุดที่ตรงกับตำแหน่ง Start ซึ่งจะได้ค่า 1 เป็นค่าเริ่มต้น
                    • แปลว่า Excel จะไล่คำนวณจากจุด Start ก่อนแล้วคิด Cell ถัดจากจุด Start ไปเรื่อยๆ จนเต็มถนนได้ในที่สุด ซึ่งผมคิดว่า Logic นี้เจ๋งมากๆ (มีความเข้าใจในลำดับ Sequence ของสูตร Excel อย่างดี ว่ามันจะไล่หาช่องที่ควรคำนวณก่อนให้เอง)
                    แกะเคล็ดวิชา Excel Wizard ในการแข่ง Speed Run Excel ระดับโลก 456

                    ซึ่งถ้าลองดูค่าตัวเลข พวกนี้คือระยะทางที่ต้องวิ่งตามกฎจราจรในเกมเลยครับ (ช่องเหลืองที่ผมระบายไว้คือจุดเริ่มต้น)

                    แกะเคล็ดวิชา Excel Wizard ในการแข่ง Speed Run Excel ระดับโลก 457

                    ส่วนที่ผมมองว่าเป็นเรื่องที่ช่วยให้เร็วมากๆ อีกอันนึงคือ คุณโบมีการ Reuse พื้นที่สูตรเดิมที่ทำไว้ใน Level ก่อนหน้า โดยปรับปรุงสูตรเพิ่มเติมแค่เรื่อง +SUMIFS() ตอนจบเพื่อให้เคสเดิมที่เคยคำนวณไว้แค่ระยะทาง สามารถรองรับเคสที่ต้องคำนวณระยะเวลาซึ่งต้องคำนึงถึงกรณีที่มีรถติดบนถนนได้ด้วยอีกต่างหาก

                    คุณโบใช้การเลือกพื้นที่แบบ Go to Special -> Formula แล้วกด F2 เพื่อแก้สูตรเพิ่มเติมแล้วกด Ctrl+Enter เพื่อใส่สูตรทีเดียวทุกช่องได้แบบไม่เสียเวลาเลย

                    =
                    IF(จุดStart=ตำแหน่งถนนนั้นๆ,1,
                    MIN(
                    SWITCH(ทิศทางถนนแนวตั้ง,"U",เวลาช่องบน,"D",เวลาช่องล่าง,999),
                    SWITCH(ทิศทางถนนแนวนอน,"R",เวลาช่องขวา,"L",เวลาช่องซ้าย,999)
                    )
                    +1)
                    +SUMIFS(ระยะเวลารอรถแต่ละแบบ,รถแต่ละแบบ,ถนนนั้นๆ)
                    แกะเคล็ดวิชา Excel Wizard ในการแข่ง Speed Run Excel ระดับโลก 458

                    ผลลัพธ์ก็จะได้ประมาณนี้ ซึ่งจะต่างจากเดิมนิดหน่อย เพราะมีการบวกเวลารถติดเข้าไปด้วยในส่วนของ SUMIFS

                    แกะเคล็ดวิชา Excel Wizard ในการแข่ง Speed Run Excel ระดับโลก 459

                    ทั้งนี้คุณโบไม่ต้องใช้สูตรเดิมแล้วเพราะได้ Copy paste value คำตอบไว้แล้วนั่นเอง

                    ซึ่งทั้งหมดนี้จะไม่ Work เลย ถ้าปราศจากการวางแผนการวาง Map และการใส่ U D L R ที่ขอบแผนที่ตั้งแต่แรก!

                    เรื่องของ Data Table

                    ทั้งๆ ที่ Data Table เป็นเครื่องมือที่ทรงพลัง และคนที่ทำ Model คำนวณใน Excel ก็ชอบใช้มันมาก ตอนไล่คำนวณคำตอบ คุณโบกลับใช้ Data Table เท่าที่จำเป็น…

                    แต่เราจะเห็นได้ว่าใน Level 3-4 จะพบว่าคุณโบมีการใช้ MINIFS ในการหาคำตอบของ Level นั้นๆ ไปเลย โดยไม่ต้องใช้ Data Table นั่นแปลว่า คุณโบได้คิดไว้แล้วว่า ถ้าเลี่ยง Data Table ได้ก็จะเลี่ยง เช่น

                    =MINIFS(ระยะทางหรือเวลาใน ds,ตำแหน่งใน ad,ตำแหน่งปลายทางที่ต้องการ)

                    คุณโบจะใช้ Data Table ในเคสที่เขียนสูตรปกติได้ยาก เช่น มีการวางแผนที่โดยสูตรในแผนที่อ้างอิงตำแหน่งเริ่มต้นและเป้าหมายเอาไว้ แล้วโจทย์ต้องการให้เปลี่ยนแปลงจุดเริ่มต้นไปเรื่อยๆ แบบนี้จะเขียนสูตรธรรมดาแทบไม่ได้เลย เพราะจะต้องทดแผนที่เยอะมาก เช่น Level 2, 5, 6 เป็นต้น

                    เช่น Level 6 ที่ยากที่สุดสูตรใน Data Table จะเป็นประมาณนี้

                    =MINIFS(เวลาใน ds,ตำแหน่งใน ad,XLOOKUP(ตำแหน่งเริ่มต้นที่เป็น column input cell, list ตำแหน่งเริ่มต้น ที่เป็น input data table ,list ตำแหน่งปลายทาง))

                    เหตุผลที่คุณโบเลี่ยง Data Table คือ Data Table มันจะมีการโยนค่าจาก Column Input / Row Input ลงไปใน Column Input Cell/ Row Input Cell ที่เราเลือก (ในที่นี้มีแค่ Column Input Cell)

                    ซึ่งพอมีการโยนค่าใหม่เข้าไป ทำให้เกิดการกระตุ้นให้สูตรทำงานอีกรอบหนึ่ง แม้บางที่มันอาจจะไม่จำเป็นต้องคำนวณซ้ำ

                    ดังนั้นเปรียบเสมือนว่า Data Table ถูกบังคับให้ทำการคำนวณ Output ออกมาใหม่ โดยจำนวนครั้งในการคำนวณจะเท่ากับจำนวนการกำหนดค่า Column Input x Row Input ของ Data Table นั้นๆ เลย ซึ่งต่างจากการเขียนสูตรที่อ้างอิงค่าจากตารางที่คำนวณไว้แล้วแบบไม่ต้องคำนวณซ้ำโดยไม่จำเป็น

                    ดังนั้นในเคสนี้ ทุกครั้งที่ Data Table คำนวณใหม่ จะต้องคำนวณพื้นที่แผนที่อย่างน้อย =71*66 = 4,686 ครั้งต่อ 1 แถวของ Data Table 20 ข้อ ก็ 93,720 ครั้ง อีกข้อนึงคือ Data Table มีความ Volatile หนักมาก คำนวนใหม่ทุกครั้งที่มีการเปลี่ยนค่าในเซลใดๆก็ตาม

                    ดังนั้นใน Calculation options ของ Formula จึงมีให้เลือกด้วยว่า Automatic Except for Data Table เพื่อไม่ให้ Data Table คำนวณใหม่ทุกครั้งที่ แก้ค่าในเซลด้วย

                    แต่ไม่ว่าจะคำนวณแบบไหน พอคำนวณเสร็จคุณโบก็ Copy Paste Value เก็บไว้เลยเพื่อเพิ่มความเร็วในการคำนวณข้อต่อไป ไม่ให้ถูกข้อเดิมถ่วงไว้

                    เทคนิคการเลือก Ribbon หรือ Menu ต่างๆ

                    • ทำลาย Conditional Format ทั้งชีทโดยกด Alt, h, l, c, e
                    • เรียกใช้ Data Table ด้วย Alt, a, w, t
                    • กด Paste Special ด้วย Ctrl+Alt+V –> กด t เพื่อเลือก Paste Format
                    • ตีกรอบด้วย Hotkey เช่น Alt, h, b, a (ตีกรอบทั้งหมด)
                    • ปลี่ยน Number Format ตัวเลข ด้วย Ctrl+Shift+เลขต่างๆ เช่น
                      • Ctrl+Shift+5 (Ctrl+%) เพื่อทำเป็น %
                      • Ctrl+Shift+4 (Ctrl+$) เพื่อทำเป็น Currency
                      • Ctrl+Shift+3 (Ctrl+#) เพื่อทำเป็นวันที่

                    อะไรเร็วได้ก็เร็ว

                    • คุณ Bo ตั้งชื่อสั้นๆ เป็นตัวย่อของพื้นที่นั้น แบบพอใช้งานได้แค่นั้นเอง เช่น mp=map, ad=address เป็นต้น ซึ่งแม้จะดูเหมือนเป็นเรื่องเล็กๆ น้อยๆ แต่การลดตัวอักษรไป 1 ตัวก็เพิ่มความเร็วได้เช่นกันจริงมะ?
                    • ตอนที่ไล่ตอบคำถามแต่ละข้อ คุณโบก็ทำการเลือกข้อมูลแล้ว Fill Down ผลลัพธ์ของเค้า แบบ Fill Down ติดเลข Score ของโจทย์ (ที่ไม่ใช่สูตร) มาด้วย
                      • ซึ่งตรงนี้เค้าสังเกตแล้วว่าแต่ละข้อคะแนนเท่ากัน จึง Ctrl+D แบบติด score มาได้แบบไม่ทำให้ผลลัพธ์ผิด
                      • ซึ่งจริงๆ ก็ดูเสี่ยงนิดหน่อยแต่ก็เป็นการเอาตัวรอดที่ชาญฉลาดมาก ไม่งั้นการเลือก Range จะยากขึ้นอีกหน่อยเพราะต้องกดเปลี่ยนจุดยึดของ Range อีกที

                    สติและสมาธิก็สำคัญ

                    มีอยู่ช่วงนึง (ประมาณนาทีที่ 36:30 ได้) คุณโบมีเขียนสูตรผิดไปนิดนึงทำให้คำตอบออกมาเป็น 0 แบบงงๆ เพราะน่าจะมีการระบุ Range แล้วดันพิมพ์สูตรติดตัว J มาแบบไม่ตั้งใจ (ทำให้มีช่วงที่หน้าคุณโบอึ้งกับคำตอบอยู่ซักพักนึง)

                    แกะเคล็ดวิชา Excel Wizard ในการแข่ง Speed Run Excel ระดับโลก 460

                    แต่สุดท้ายคุณโบก็สามารถตั้งสติแก้สูตรให้ถูกต้องได้ ซึ่งถือว่าดึงสติกลับมาได้เร็วมากๆ ทั้งๆ ที่เค้าน่าจะกำลังตื่นเต้นกับการแข่งขันมากๆ แท้ๆ

                    นี่ถ้าคุณโบเขียนไม่พลาดเลย น่าจะเร็วกว่านี้เป็นนาทีเลย สุดยอด!

                    คุณโบฝากบอกผู้อ่าน

                    คุณโบมีสิ่งที่อยากฝากบอกทุกคนที่อยากใช้ Excel ให้เก่งขึ้นว่านอกจากเทคนิคต่างๆ ข้างบนแล้ว สิ่งสำคัญที่สุดจริงๆ ก็คือเรื่องสุดท้ายนี้ครับ

                    ให้ฝึกฝนบ่อยๆ ทำโจทย์แปลกๆใหม่ๆ เพิ่มความยากของโจทย์ไปเรื่อยๆ

                    Bo Rybobon (Excel Wizard)

                    นี่ล่ะครับ หนทางของผู้ที่เก่งกาจได้อย่างแท้จริง ก็ยังต้องใช้ความฉลาดควบคู่กับการฝึกฝนอย่างสม่ำเสมอด้วยนั่นเอง

                    และถ้าใครอยากรู้ Shortcut Excel มากกว่านี้ ก็สามารถศึกษาได้จากบทความของผมได้ครับ (ฝากความรู้ให้นิดนึง อิอิ) https://www.thepexcel.com/remember-excel-keyboard-shortcut/

                  • เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2

                    ตอนนี้เป็นเนื้อหาที่ต่อจาก https://www.thepexcel.com/calculate-dax-in-depth/ ซึ่งจำเป็นจะต้องอ่านก่อน ไม่งั้นไม่มีทางเข้าใจบทความนี้ได้เลยครับ ถ้าอ่านบทความก่อนจบแล้ว ก็เชิญมาต่อที่นี่ได้เลย

                    ใน Part2 นี้ผมจะปูความรู้ 2 เรื่องที่สำคัญมากๆ นั่นคือ Context Transition และ CALCULATETABLE ให้ก่อน ใน Part ถัดไปจะเอาทุกอย่างมายำกันละ

                    Context Transition

                    ทำความเข้าใจ Context Transition เบื้องต้น

                    ปกติแล้วถ้าเราเขียนสูตรใน New Column ของตาราง มันจะไม่มี Filter อะไรเกิดขึ้นเลย ดังนั้นถ้าเราอยู่ที่ตาราง Store แล้วเขียน SUM Quantity ก็จะได้ค่าเท่ากันหมดเลยเสมอ ซึ่งก็คือการ SUM คอลัมน์ที่เราเลือกทั้งตารางนั่นเอง

                    Store Qty = SUM(OrderDetail[Quantity])
                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2 461

                    แต่ถ้าหากเราเอา CALCULATE ไปครอบสูตรนั้น ผลลัพธ์จะเปลี่ยนไป กลายเป็นว่าเราได้ Qty เฉพาะร้านค้านั้นๆ

                    Store Qty = CALCULATE(SUM(OrderDetail[Quantity]))
                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2 462

                    สาเหตุคือ CACLCULATE จะทำการเปลี่ยนค่าในแต่ละคอลัมน์ของแถวนั้นๆ ทุกคอลัมน์ (Row Context) ให้กลายเป็น Filter Context ซึ่งกระบวนการนี้เรียกว่า Context Transition (นั่นคือจะมี Filter เกิดขึ้นมากมายตามจำนวนคอลัมน์ของข้อมูลเลย)

                    ทำให้เดิมทีที่ไม่มี Filter ใน Table กลายเป็นมี Filter ก่อนจะคำนวณ SUM(OrderDetail[Quantity]) ออกมา เราก็เลยได้ Quantity เฉพาะร้านค้านั้นๆ ไปโดยปริยาย ซึ่งก็เจ๋งดี

                    แต่ความแปลกที่แท้จริง คือ ถ้าเราเปลี่ยนสูตรไปอ้างอิง Measure Total Qty แทน ซึ่งมีสูตรดังนี้

                    Total Qty = SUM(OrderDetail[Quantity])

                    เช่น ในคอลัมน์ Store Qty เราเปลี่ยนสูตรเป็นแบบนี้

                    Store Qty = [Total Qty]

                    เราจะได้ผลลัพธ์เหมือนมี CALCULATE มาครอบทันที!! ซึ่งเกิดขึ้นเพราะว่าเมื่อใดก็ตามที่เราอ้างอิง Measure มันจะแอบเอา CALCULATE มาครอบให้โดยอัตโนมัติ!

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2 463

                    Context Transition ใน Measure แบบพื้นฐาน

                    อย่างไรก็ตาม ปกติแล้วเราจะไม่ค่อยใช้ Context Transition ในตารางที่มีตัวตนจริงๆ หรอก แต่เราจะใช้ในตารางจำลองที่สร้างใน Measure อีกทีต่างหาก โดยเฉพาะเวลาใช้กับ Iterator เช่น SUMX, MAXX, RANKX, FILTER, ADDCOLUMNS อะไรแบบนี้เป็นต้น

                    พฤติกรรมแบบนี้ทำให้เราสามารถเขียนสูตรที่ซับซ้อนได้โดยเขียนสูตรที่สั้นขึ้นมากๆ เช่น สมมติผมต้องการคำนวณ Total Qty สูงสุดในระดับ Product ผมสามารถเขียน Measure สั้นๆ แบบนี้ได้เลย

                    MAX Qty per Product = MAXX('Product',[Total Qty])
                    • สิ่งที่มันทำคือจะสร้างตาราง Product ขึ้นมาในสูตร (1 บรรทัด 1 Product)
                    • แล้ว Add Column จำลองใหม่ขึ้นมา โดยใช้สูตร [Total Qty]
                    • ซึ่งจะได้ Quantity รวมโดยมี Context Transition เกิดขึ้น
                    • คอลัมน์จำลอง เลยได้ Quantity รวมในระดับ Product เกิดขึ้นมาในแต่ละแถว
                    • จากนั้นหาค่ามากสุดออกมาเพราะเราใช้ MAXX (ตอนจบ หาค่ามากสุดของคอลัมน์จำลอง)

                    แล้วเราก็สามารถเอา Measure นี้ไปใช้ในรายงาน โดย Filter ด้วยอะไรก็ได้ (ตราบเท่าที่มัน Filter เจ้าตัว [Total Qty] ได้)

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2 464

                    แต่ถ้าเราต้องการ Max Total Qty ในระดับที่ใหญ่กว่าระดับที่เรามีในตารางจริง เราจะใช้การอ้างอิงตารางทื่อๆ ไม่ได้ แต่ต้องสร้างตารางจำลองขึ้นมาในสูตรแทน ซึ่งทำได้ง่ายๆ ด้วย DISTINCT หรือ VALUES นั่นเอง เช่น

                    ผมต้องการ คำนวณ Total Qty สูงสุดในระดับ Brand ผมก็เขียนแบบนี้ได้เลย

                    MAX Qty per Brand = MAXX(DISTINCT('Product'[Brand]),[Total Qty])
                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2 465

                    ซึ่งจริงๆ แล้วต่อให้เราอยากได้ในระดับ Product หากเราใช้ DISTINCT หรือ VALUES จะทำให้คำนวณเร็วกว่าการอ้างอิงทั้งตารางแน่นอน ดังนั้นจึงสามารถแก้สูตรเดิมเป็นอันนี้ก็จะดีขึ้นครับ

                    MAX Qty per Product = MAXX(DISTINCT('Product'[ProductKey]),[Total Qty])

                    ทำความเข้าใจสิ่งที่เกิดขึ้น

                    สมมติผมจะวิเคราะห์ผลลัพธ์ใน MAX Qty per Brand ของลูกค้าทวีป Asia ในรายงาน ด้วยสูตรนี้

                    MAX Qty per Brand = MAXX(DISTINCT('Product'[Brand]),[Total Qty])

                    หลังจากมันสร้างตารางจำลองระดับ Brand ขึ้นมา ให้ 1 บรรทัดคือ 1 Brand แล้วสร้างคอลัมน์ใหม่ที่ใส่สูตรว่า [Total Qty] ลงไป

                    สมมติว่า ผมจำลองการมองที่บรรทัดของ “ตารางจำลอง” ที่ Brand เป็น Litware ให้ดู

                    แบ่งกลุ่ม

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                      • Customer[Cust Continent]=”Asia”
                    2. Row Context ดั้งเดิม
                      • ‘Product'[ฺBrand]=”Litware” (เกิดในตารางจำลอง)
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                      • ไม่มี เพราะสูตรมีแค่ CALCULATE(SUM(OrderDetail[Quantity]))
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER
                      • ไม่มี เพราะสูตรมีแค่ CALCULATE(SUM(OrderDetail[Quantity]))

                    ขั้นตอนการทำงาน

                    • เก็บ Filter Argument เอาไว้ในใจ
                      • ไม่มี
                    • Copy Filter Context เดิมมาเป็น New Filter Context
                      • Customer[Cust Continent]=”Asia”
                    • Row Context
                      • ‘Product'[ฺBrand]=”Litware => ถูกทำให้เป็นส่วนหนึ่งของใน New Filter Context
                    • CALCULATE Modifier
                      • ไม่มี
                    • เอา Filter Argument ที่เก็บไว้มาใช้ สรุปแล้ว New Filter Context ได้แบบนี้
                      • Customer[Cust Continent]=”Asia”
                      • ‘Product'[ฺBrand]=”Litware

                    ดังนั้นในตารางจำลองบรรทัดที่เป็น Litware จึงมีผล Filter จากทั้ง Brand Litware และจากทวีป Asia ด้วยนั่นเอง

                    ดังนั้น ตารางจำลองที่ถูกสร้างขึ้นมามันจะคล้ายกับ Table อันนี้มากๆ ซึ่งค่าที่มากสุดคือ 38,384 นั่นเอง

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2 466

                    แปลว่าคอลัมน์ใหม่ในตารางจำลองทั้งหมด ได้รับผล Filter จากทวีปนั้นๆ อย่างถูกต้อง จึงออกมาเป็น Total Qty ภายใต้ทวีปและ brand นั้นๆ ผลลัพธ์ตอน MAXX ที่ได้จึงเป็น Max Total Qty per Brand ภายใต้แต่ละทวีปอย่างถูกต้อง ซึ่งก็คือ 38,384 นั่นเองครับ

                    Context Transition ใน Measure แบบซับซ้อนขึ้น

                    ทีนี่ถ้าใน Measure ที่เราทำ Context Transition ดันมี CALCULATE ที่มีเงื่อนไขอยู่แล้วล่ะ? บอกไว้ก่อนว่าอันนี้ทำเพื่อการศึกษาเท่านั้น ในชีวิตจริงบาง Measure ที่เขียนในนี้จะดูไม่ Make Sense มากๆ 55

                    Case ที่ 1 : เขียนเงื่อนไขที่ไม่ซ้ำกับ Row Context

                    ถ้าเราใช้ Measure นี้ใน Context Transition

                    Person Cust Total Qty = CALCULATE([Total Qty],Customer[Customer Type]="Person")

                    เช่นสมมติว่า ผมเขียน Measure ที่จะหาว่า Quantity รวมของลุกค้าบุคคลที่เยอะที่สุดต่อ 1 Brand คือเท่าไหร่ ซึ่งเขียนได้แบบนี้

                    MAX Person Qty per Brand = MAXX(DISTINCT('Product'[Brand]),[Person Cust Total Qty])

                    ดังนั้นการวิเคราะห์ การมองที่บรรทัดของ “ตารางจำลอง” ที่ Brand เป็น Litware จะเป็นแบบนี้

                    แบ่งกลุ่ม

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                      • Customer[Cust Continent]=”Asia”
                    2. Row Context ดั้งเดิม
                      • ‘Product'[ฺBrand]=”Litware” (เกิดในตารางจำลอง)
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                      • Customer[Customer Type]=”Person” (จาก CALCULATE ใน Measure)
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER
                      • ไม่มี

                    ขั้นตอนการทำงาน

                    • เก็บ Filter Argument เอาไว้ในใจ
                      • Customer[Customer Type]=”Person”
                    • Copy Filter Context เดิมมาเป็น New Filter Context
                      • Customer[Cust Continent]=”Asia”
                    • Row Context
                      • ‘Product'[ฺBrand]=”Litware => ถูกทำให้เป็นส่วนหนึ่งของใน New Filter Context
                    • CALCULATE Modifier
                      • ไม่มี
                    • เอา Filter Argument ที่เก็บไว้มาใช้ สรุปแล้ว New Filter Context ได้แบบนี้
                      • Customer[Cust Continent]=”Asia”
                      • ‘Product'[ฺBrand]=”Litware
                      • Customer[Customer Type]=”Person”

                    ดังนั้นในตารางจำลองบรรทัดที่เป็น Litware จึงมีผล Filter จากทั้ง Brand Litware และจากทวีป Asia และมาจากลูกค้าบุคคล ด้วยนั่นเอง

                    Case ที่ 2 : มีการปลด Filter ที่ Row Context ออก

                    ถ้าเราใช้ Measure นี้ใน Context Transition

                    Total Qty All Brand = CALCULATE([Total Qty],REMOVEFILTERS('Product'[Brand]))

                    เช่นสมมติว่า ผมเขียน Measure ที่จะหาว่า Quantity รวมของลุกค้าบุคคลที่เยอะที่สุดต่อ 1 Brand คือเท่าไหร่ ซึ่งเขียนได้แบบนี้

                    MAX Person Qty per Brand = MAXX(DISTINCT('Product'[Brand]),[Total Qty All Brand])

                    ดังนั้นการวิเคราะห์ การมองที่บรรทัดของ “ตารางจำลอง” ที่ Brand เป็น Litware จะเป็นแบบนี้

                    แบ่งกลุ่ม

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                      • Customer[Cust Continent]=”Asia”
                    2. Row Context ดั้งเดิม
                      • ‘Product'[ฺBrand]=”Litware” (เกิดในตารางจำลอง)
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                      • ไม่มี
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER
                      • REMOVEFILTERS(‘Product'[Brand])

                    ขั้นตอนการทำงาน

                    • เก็บ Filter Argument เอาไว้ในใจ
                      • ไม่มี
                    • Copy Filter Context เดิมมาเป็น New Filter Context
                      • Customer[Cust Continent]=”Asia”
                    • Row Context
                      • ‘Product'[ฺBrand]=”Litware => ถูกทำให้เป็นส่วนหนึ่งของใน New Filter Context
                    • CALCULATE Modifier
                      • REMOVEFILTERS(‘Product'[Brand]) ทำให้ New Filter Context เรื่อง Brand หายไป
                    • เอา Filter Argument ที่เก็บไว้มาใช้ สรุปแล้ว New Filter Context ได้แบบนี้
                      • Customer[Cust Continent]=”Asia”

                    ดังนั้นในตารางจำลองบรรทัดที่เป็น Litware จึงมีผล Filter จากทวีป Asia อย่างเดียว ผลที่ได้จึงไม่ต่างจากการใช้ Total Qty เฉยๆ นั่นเอง

                    Case ที่ 3 : เขียนเงื่อนไขซ้ำกันกับ Row Context

                    ถ้าเราใช้ Measure นี้ใน Context Transition

                    Contoso Total Qty = CALCULATE([Total Qty],'Product'[Brand]="Proseware")

                    เช่นสมมติว่า ผมเขียน Measure ที่จะหาว่า Quantity รวมของ Brand Proseware ที่เยอะที่สุดต่อ 1 Brand คือเท่าไหร่ ซึ่งเขียนได้แบบนี้

                    MAX Proseware Qty per Brand = MAXX(DISTINCT('Product'[Brand]),[Proseware Total Qty])

                    ดังนั้นการวิเคราะห์ การมองที่บรรทัดของ “ตารางจำลอง” ที่ Brand เป็น Litware จะเป็นแบบนี้

                    แบ่งกลุ่ม

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                      • Customer[Cust Continent]=”Asia”
                    2. Row Context ดั้งเดิม
                      • ‘Product'[ฺBrand]=”Litware” (เกิดในตารางจำลอง)
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                      • ‘Product'[Brand]=”Proseware” (จาก CALCULATE ใน Measure)
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER
                      • ไม่มี

                    ขั้นตอนการทำงาน

                    • เก็บ Filter Argument เอาไว้ในใจ
                      • ‘Product'[Brand]=”Proseware”
                    • Copy Filter Context เดิมมาเป็น New Filter Context
                      • Customer[Cust Continent]=”Asia”
                    • Row Context
                      • ‘Product'[ฺBrand]=”Litware => ถูกทำให้เป็นส่วนหนึ่งของใน New Filter Context
                    • CALCULATE Modifier
                      • ไม่มี
                    • เอา Filter Argument ที่เก็บไว้มาใช้ สรุปแล้ว New Filter Context ได้แบบนี้
                      • Customer[Cust Continent]=”Asia”
                      • ‘Product'[Brand]=”Proseware” ซึ่งจะมาทับ Litware ไปเลยเนื่องจากอ้างอิงถึง Field เดียวกัน

                    ดังนั้นในตารางจำลองบรรทัดที่เป็น Litware จึงมีผล Filter จากทั้ง Brand Prosware และจากทวีป Asia นั่นเอง

                    ผลลัพธ์ของการใช้ Measure ทั้ง 3 จะเป็นแบบนี้

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2 467

                    การใช้ CALCULATETABLE

                    ทำความรู้จัก CALCULATETABLE

                    CALCULATETABLE คือฟังก์ชันที่มีความสามารถเปลี่ยน Filter Context ได้ ซึ่งคล้าย CALCULATE มากๆ ต่างกันนิดเดียวคือ CALCULATETABLE ต้องให้ผลลัพธ์ออกมาเป็นตาราง แทนที่จะเป็นค่าค่าเดียวแบบที่ CALCULATE ทำได้

                    CALCULATETABLE ( <ตาราง> , [ Filter1 ] , [ Filter2],... )

                    ยกตัวอย่างเช่น ถ้าผมต้องการตาราง OrderDetail เฉพาะรายการที่มาจากลูกค้าบริษัท และมาจาก ร้านค้าทวีป Asia และซื้อสินค้าที่ราคาแพงกว่า 1000 บาท ผมสามารถเขียนแบบนี้ได้เลย

                    Person Order in Asia Store = 
                    CALCULATETABLE(OrderDetail,
                        Customer[Customer Type]="Person",
                        Store[Store Continent]="Asia",
                        'Product'[Unit Price]>1000
                        )

                    ซึ่งผลลัพธ์ที่ได้จะออกมาเป็นตาราง คล้ายกับการใช้ FILTER เลย แต่ว่าผมสามารถเขียนเงื่อนไขจาก Field ไหนก็ได้ใน Model โดยไม่ต้องใช้ RELATED เหมือนกรณีทำด้วย FILTER ด้วยซ้ำ

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2 468

                    สำหรับข้อเสียของ CALCULATETABLE ก็เป็นเช่นเดียวกับ CALCULATE คือ การเขียนอ้างอิงเงื่อนไข จะต้องอ้างถึงคอลัมน์ที่มีตัวตนใน Data Model เท่านั้น เช่น ผมจะเขียนเงื่อนไขว่าเอาเฉพาะรายการที่อัตรากำไรเกิน 60% แบบนี้ไม่ได้ เพราะผมไม่ได้มีคอลัมน์นั้นจริงๆ (แต่ FILTER ทำได้)

                    การใช้ CALCULATETABLE ปกติเค้าไม่ได้ใช้สร้างตารางที่มีตัวตนจริงๆ หรอกครับ แต่เค้าใช้สร้างตารางจำลองใน Measure มากกว่า ซึ่งจะมีประโยชน์มากๆ เลย และในเมื่อมันให้ผลลัพธ์เป็นตาราง มันจึงสามารถใช้เป็นเงื่อนไขของ CALCULATE ได้ด้วย!!

                    จริงๆ แล้วเราก็สามารถใช้ Table Function อะไรก็ตามใน CALCULATETABLE ได้หมดเลย รวมถึงเงื่อนไข Filter ของ CALCULATETABLE จริงๆ แล้วก็เป็นตารางเช่นเดียวกับ CALCULATE ด้วยนั่นเอง ดังนั้นสูตรข้างบนสามารถเขียนได้แบบนี้ (เหมือนกรณี CALCULATE)

                    Person Order in Asia Store = 
                    CALCULATETABLE(OrderDetail,
                        FILTER(ALL(Customer[Customer Type]),Customer[Customer Type]="Person"),
                        FILTER(ALL(Store[Store Continent]),Store[Store Continent]="Asia"),
                        FILTER(ALL('Product'[Unit Price]),'Product'[Unit Price]>1000)
                        )

                    ซึ่ง Performance จะดีกว่าการ FILTER ตรงๆ ด้วย เนื่องจากมีการใช้ ALL ทำให้เห็นค่าไม่ซ้ำก่อนจะคิดเงื่อนไข ทำให่การ Scan ใช้เวลาน้อยลง

                    จริงๆ แล้ว Time Intelligence นั้นแฝงไปด้วย CALCULATETABLE

                    ฟังก์ชัน Time Intelligence ถ้ามีการอ้างอิงถึงคอลัมน์ Date ใน Date Table จริงๆ นั้นแฝงไปด้วย CALCULATETABLE อยู่นะ เช่น

                    เช่น SAMEPERIODLASTYEAR ที่ดูง่ายๆ เหมือนไม่มีอะไรนั้น หาเขียนแบบนี้

                    SAMEPERIODLASTYEAR(dDate[Date])

                    จะหมายถึง

                    SAMEPERIODLASTYEAR( CALCULATETABLE ( DISTINCT (dDate[Date]) ) )

                    ซึ่งก็จะให้ผลลัพธ์เป็นตารางที่ย้อนเวลากลับไป 1 ปีนั่นเอง (และอาจมี Context Transition ได้)

                    Case ซับซ้อน :

                    อยากได้ยอดปีก่อนหน้า โดยสนใจวันประจำสัปดาห์ ด้วย CALCULATETABLE

                    สมมติว่าผมต้องการแสดงยอด Total Qty เฉพาะวันที่เลือกในแต่ละปีของ Brand ที่สนใจ แล้วผมเลือก Slicer วันจันทร์จะได้แบบนี้

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2 469

                    ทีนี้ผมอยากเอาปีมาเป็น Slicer แล้วเลือกปี 2020 ไว้ จากนั้นผมอยากจะแสดงยอดของปีก่อนหน้าด้วย จึงใช้ SAMEPERIODLASTYEAR ทำ Time Intelligence ออกมา ได้แบบนี้ แล้วลองเอาไปใส่ในรายงานที่ต้องการ

                    Total Qty Prev Year = CALCULATE([Total Qty],SAMEPERIODLASTYEAR(dDate[Date]))

                    ทำไม Time Intelligence ปกติถึงไม่ Work?

                    ปรากฏว่าเลขออกมาไม่ถูกต้อง!! เพราะเลข Prev Year ที่ได้ไม่เห็นตรงกับของปี 2019 ในตารางข้างบนเลย??

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2 470

                    สาเหตุที่ไม่ตรง เป็นเพราะ SAMEPERIODLASTYEAR เริ่มต้นจาก Filter Context ที่เป็นวันจันทร์ทั้งหมด แล้วย้อนเวลากลับไป 1 ปี ให้ลงเลขวันเดิม ซึ่งอาจจะไม่ใช่วันจันทร์แล้วนั่นเอง ดังนั้นผลที่ได้จึงไม่ใช่วันจันทร์ของปี 2019

                    แล้วถ้าเราต้องการวันจันทร์ของปีก่อนหน้าด้วยจริงๆ เราจะทำยังไงล่ะ?

                    ผมจะลองใช้วิธีสร้างตารางวันที่ในปีปัจจุบันทั้งหมด แล้วย้อนเวลากลับไป 1 ปีจากนั้นค่อยเลือกให้เป็นวันจันทร์ โดยผมจะใช้วิธีสร้างตัวแปรมาเก็บค่าก่อน จะได้ไม่ซับซ้อนจนเกินไป แล้วลองใช้ COUNTROWS เช็คดูว่าผลลัพธ์เป็นอย่างไร

                    สมมติผมเขียนแบบนี้ ว่าให้ลองดูคอลัมน์วันที่แล้วนับจำนวนแถวออกมา

                    Total Qty Prev Year SelectedDay = 
                    VAR PrevYearDate =CALCULATETABLE(DISTINCT(dDate[Date]))
                    RETURN COUNTROWS(PrevYearDate)

                    ผลจะได้ออกมาเป็น 52 เพราะในปี 2020 ที่เลือกอยู่นั้นมีวันจันทร์ 52 วันนั่นเอง

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2 471

                    ทีนี้ผมต้องการจะทำการปลด Filter เรื่องวันออกไปซะ ก่อนจะทำการ DISTINCT วันออกมา ดังนั้นสามารถใช้ CALCULATETABLE เขียนแบบนี้ได้

                    Total Qty Prev Year SelectedDay = 
                    VAR PrevYearDate =CALCULATETABLE(DISTINCT(dDate[Date]),REMOVEFILTERS(dDate[DayName]))
                    RETURN COUNTROWS(PrevYearDate)

                    ผลลัพธ์จำนวนวันที่จะได้ 366 วัน นั่นคือจำนวนวันในปี 2020 นั่นเอง

                    ทีนี้ผมจะทำการย้อนเวลากลับไป 1 ปี ก็เลวอยากใช้ SAMEPERIODLASTYEAR หรือ DATEADD มาช่วย

                    แต่ผมจะสร้างตัวแปรใหม่แบบนี้ไม่ได้นะ เพราะตัวแปรใน VAR มันจะถูกคำนวณทิ้งไว้จนกลายเป็นค่าคงที่แล้ว ถึงจะเปลี่ยน Filter Context ก่อนคำนวณค่าคงที่ก็ไม่มีผลอะไร (มันจะได้ 366 วันซึ่งผิด)

                    Total Qty Prev Year SelectedDay = 
                    VAR PrevYearDate =CALCULATETABLE(DISTINCT(dDate[Date]),REMOVEFILTERS(dDate[DayName]))
                    VAR PrevYearDate2=CALCULATETABLE(PrevYearDate,SAMEPERIODLASTYEAR(dDate[Date]))
                    RETURN COUNTROWS(PrevYearDate2)

                    วิธีที่ถูกต้องคือต้องเขีนยสูตรที่สามารถเปลี่ยน Filter Context ตอนคิดตัวแปรแรกเลย

                    แต่จะเขียนแค่นี้ก็ไม่ Work อีก…

                    Total Qty Prev Year SelectedDay =
                    VAR PrevYearDate =
                        CALCULATETABLE (
                            DISTINCT ( dDate[Date] ),
                            REMOVEFILTERS ( dDate[DayName] ),
                            SAMEPERIODLASTYEAR ( dDate[Date] )
                        )
                    RETURN
                        COUNTROWS ( PrevYearDate )

                    สาเหตุที่ไม่ Work เราสามารถใช้วิธีการคิดของ CALCULATE มาพิจารณาได้ครับ เช่น

                    แบ่งกลุ่ม

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                      • dDate[Year]=2020
                      • dDate[DayName]=”Monday”
                    2. Row Context ดั้งเดิม
                      • ไม่มี
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                      • SAMEPERIODLASTYEAR ( dDate[Date] )
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER
                      • REMOVEFILTERS ( dDate[DayName] ) ที่เราตั้งใจเขียน
                      • REMOVEFILTERS ( dDate ) ที่แถมมาจาก Time Intelligence เช่น SAMEPERIODLASTYEAR

                    ขั้นตอนการทำงาน

                    • เก็บ Filter Argument เอาไว้ในใจ
                      • SAMEPERIODLASTYEAR ( dDate[Date] ) อันนี้มันจะมองวันจันทร์ปี 2020 ก่อน แล้วย้อนกลับไป 1 ปี ซึ่งจะไม่ใช่วันจันทร์ (ปัญหาเดิม)
                    • Copy Filter Context เดิมมาเป็น New Filter Context
                      • dDate[Year]=2020
                      • dDate[DayName]=”Monday”
                    • Row Context
                      • ไม่มี
                    • CALCULATE Modifier
                      • REMOVEFILTERS ( dDate[DayName] )
                      • REMOVEFILTERS ( dDate )
                      • สรุปแล้ว Filter ในตาราง Expanded ของ dDate หายหมด
                    • เอา Filter Argument ที่เก็บไว้มาใช้ สรุปแล้ว New Filter Context ได้แบบนี้
                      • SAMEPERIODLASTYEAR ( dDate[Date] ) (ปัญหาเดิม)

                    นี่คือสาเหตุที่ไม่ Work ครับ เพราะเรายังติดปัญหาเดิมอยู่ดี Step การทำงานของมันไม่ได้ทำงานตามลำดับที่เราคาดหวังไว้ เพราะตัว SAMEPERIODLASTYEAR มันมาจำกัดวันที่ให้เห็นแค่วันจันทร์ ในขณะที่จริงๆ ผมอยากได้วันที่ของปีที่แล้วแบบไม่สนใจวันประจำสัปดาห์

                    วิธีที่ Work

                    ถ้าเราเข้าใจลำดับการคำนวณเวลาใช้ CALCULATE ซ้อนกันดีแล้ว และเข้าใจพฤติกรรมของ VAR แล้ว เราจะสามารถกำหนดลำดับที่ถูกต้องได้แบบนี้

                    แม้การคิดสูตรใดๆ ก็ตามจะต้องคิดที่ตัวในสุดก่อน แต่ CALCULATE หรือ CALCULATETABLE ตัวในสุดมันจะมองสภาพแวดล้อมของ Filter Context ที่มันทำงานอยู่ (ซึ่งมีผลจาก CALCULATE/CALCULATETABLE ตัวนอกได้) ดังนั้นสรุปแล้ว เวลาคิดเราจะต้องไล่เปลี่ยน Filter ของ CALCULATETABLE ชั้นนอกก่อน แล้วค่อยไล่เปลี่ยนชั้นใน ดังนั้นเราสามารถเขียนแบบนี้ได้เลย

                    Total Qty Prev Year SelectedDay = 
                    VAR MonthList=DISTINCT(dDate[Month])      //Step1 เก็บค่าเดือน Context ปัจจุบันไว้เป็นค่าคงที่ 
                    VAR DayList=DISTINCT(dDate[DayName])      //Step1 เก็บค่าวัน Context ปัจจุบันไว้เป็นค่าคงที่
                    VAR PrevYearDate =
                        CALCULATETABLE(
                            CALCULATETABLE (
                            CALCULATETABLE (
                                DISTINCT ( dDate[Date] ),
                                MonthList,                           //Step4 ทำให้เห็นเฉพาะวันที่เห็นใน Context
                                DayList                              //Step4 ทำให้เห็นเฉพาะเดือนที่เห็นใน Context
                            ),
                            SAMEPERIODLASTYEAR ( dDate[Date] )),     //Step3 ย้อน 1 ปี เห็นวันที่ปีก่อนทั้งปีเสมอ
                            ALLEXCEPT(dDate,dDate[Year])             //Step2 เห็นวันที่ในปีปัจจุบันทั้งปีเสมอ
                        )
                    VAR Result= CALCULATE([Total Qty],PrevYearDate)  //Step5 เอาตารางวันที่ในปีก่อนหน้ามาคำนวณ
                    RETURN Result

                    แปลว่าขั้นตอนการคิดจะเป็นแบบนี้ นั่นคือไล่ Filter Context จากชั้นนอกไปใน

                    • ตัวแปร MonthList และ DayList เก็บค่าเป็นตาราง ณ Context แรกสุดเอาไว้ก่อนเป็นค่าคงที่ ไม่เปลี่ยนแปลง
                    • ALLEXCEPT ( dDate, dDate[Year] ) มันจะปลด Filter ทุกอย่างยกเว้นปี ทำให้เห็นเฉพาะปีปัจจุบัน แบบเต็มปีเสมอ
                    • SAMEPERIODLASTYEAR ( dDate[Date] ) ในเมื่อเดิมมองเห็นเต็มปีแล้ว ก็จะย้อนกลับไป 1 ปี แบบเต็มปีด้วย
                    • จากสั้นใส่ Filter ตาม MonthList และ DayList เข้าไป เพื่อให้เหลือช่วงเดือนและวันประจำสัปดาห์ที่ถูกต้อง
                    • แล้วค่อย List คอลัมนืวันที่ออกมาเป็นตารางด้วย DISTINCT ( dDate[Date] ) แล้วเก็บไว้ในตัวแปร PrevYearDate
                    • แล้วเอาตัวแปร PrevYearDate ไปใช้ใน CALCULATE เพื่อคำนวณ [Total Qty] ออกมาเป็นอันจบเลย

                    สุดท้ายเราจะได้ค่าที่ถูกต้องดังนี้ เหนื่อยเลย

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 2 472

                    อย่างไรก็ตาม วิธีคิดอาจมีหลายแบบ ถ้าใครลองวิธีไหนแล้วได้ผลลัพธ์ที่น่าสนใจ ลองเอามาแชร์กันได้ครับ

                    สำหรับบทความนี้ยาวและยากมากๆ แล้ว ผมขอจบเท่านี้ก่อน

                    ตอนต่อไป

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

                  • วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API

                    ก่อนหน้านี้ผมได้เคยเขียนบทความวิธีใช้ Power Query ดึงข้อมูลจาก Web API ไปแล้ว 3 ตอน ถ้าใครยังไม่เคยเข้าไปอ่านก็สามารถเข้าไปอ่านดูก่อนได้นะครับ (ไม่อ่านก็ยังสามารถทำตามในบทความนี้ได้อยู่)

                    ซึ่งบทความในวันนี้เนื้อหาจะคล้ายกับตอนที่ 2 ของเดิมมากๆ คือเป็น Web API แบบ Post แต่จะเป็นกรณีตัวอย่างการใช้กับ EMS Tracking ของไปรษณีย์ไทยโดยเฉพาะ ซึ่งหลายๆ คนน่าจะอยากใช้งานอยู่ ซึ่งวิธีทำนี้จะใช้ใน Excel หรือ Power BI ก็ได้นะครับ (พอดีมีลูกค้าที่เรียน Power BI กับผมถามมา เลยเอามาเขียนเป็นบทความเผื่อเพื่อนๆ คนอื่นด้วยเลยดีกว่า)

                    เพื่อไม่ให้เสียเวลา เรามาเริ่มดูขั้นตอนกันเลยละกัน

                    สมัครสมาชิกแล้วเอา Token มาก่อน

                    ขั้นตอนแรกที่จำเป็นเลยคือเข้าไปในเว็บ https://track.thailandpost.co.th/ จากนั้นสมัครสมาชิกแล้วเข้าสู่ระบบให้เรียบร้อย (ขอไม่ลงรายละเอียดวิธีสมัครนะ)

                    จากนั้นให้เข้าหน้า Dashboard ในส่วนของ <>สำหรับนักพัฒนา แล้วกด Create Token จากนั้น Copy Token ปัจจุบันออกมาซะ

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 473

                    ศึกษาคู่มือซักหน่อย

                    ผมเองก็ดูข้อมูลตามที่บอกในคู่มือ ในส่วนของ API REST แล้วก็ทำตามได้ดังนี้ (คู่มือมีจุดที่เขียนแล้วสับสนตรง Token ให้ดูตัวอย่างผมแทนได้)

                    GetToken สำหรับการรับส่งข้อมูล

                    โดย Token รับส่งข้อมูลนี้เป้นคนละตัวกับ Token จากการสมัครสมาชิกนะ… และจะมีการกำหนดวันหมดอายุ 1 เดือน

                    ให้เรา Get Data from Web แบบ Advanced แล้วใส่ดังนี้

                    • URL Parts : https://trackapi.thailandpost.co.th/post/api/v1/authenticate/token
                    • HTTP Request Header
                      • Authorization : คำว่า Token ตามด้วยเว้นวรรค 1 ที ตามด้วย Token ในหน้าสมาชิก
                      • Content-Type : application/json
                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 474

                    จากนั้นกด ok แล้วกด Connect

                    มันก็จะทำการเข้าสู่ Power Query Editor แล้วได้ผลลัพธ์มาเป็น Token รับส่งข้อมูล ซึ่งจะยาวกว่า Token ในหน้าสมาชิกมากๆ ให้เรา Copy เก็บเอาไว้ใช้ได้ (Token รับส่งข้อมูลหมดอายุใน 1 เดือน)

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 475

                    พอเราได้ Token รับส่งข้อมูลแล้ว คราวนี้เราถึงสามารถทำการค้นหาตามพวกรหัส EMS ได้ซักที

                    สร้าง Query ดึงข้อมูลจากรหัส Tracking

                    ให้เราสร้าง Blank Query (Get Data -> From Other Source -> Blank Query) ขึ้นมาแล้วเปลี่ยน Code ใน Advanced Editor ให้เป็นดังนี้

                    let
                    
                        BarCodeList={"EDXXXXXXXX","RLXXXXXXXX","EDXXXXXXXX"}, //List เก็บรหัส Track
                        DataToken="xxxxxxx",  //Token รับส่งข้อมูลยาวๆ 
                        MyRecord=[status="all",language="TH",barcode=BarCodeList], //Record ที่จะ Post เข้า API
                    
                        Source = Json.Document(Web.Contents("https://trackapi.thailandpost.co.th/post/api/v1/track", 
                        [
                            Headers=
                                [
                                Authorization="Token "&DataToken,
                                #"Content-Type"="application/json"
                                ],
                            Content=Json.FromValue(MyRecord)                  
                        ] 
                    
                        ))
                    in
                        Source

                    จากนั้นเปลี่ยนสิ่งที่ระบุใน BarCodeList และ DataToken ตามต้องการ เช่น

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 476

                    จากนั้นพอ Run Query จะได้ดังนี้ แสดงว่าสำเร็จ

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 477

                    ดูข้อมูลที่ Response กลับมา

                    จากนั้นเราก็กดเข้าไปใน Record ที่ response กลับมา 1 ที แล้วกด Record ที่ items อีกที แล้วค่อยกด Convert เป็น Table

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 478

                    จากนั้นให้ Expand List ออกมาเป็นหลายๆแถว

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 479

                    จากนั้นค่อย Expand Record ออกมาโดยเลือกเอาเฉพาะคอลัมน์ที่ต้องการได้เลย

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 480

                    แล้วเราก็จะได้ Data ที่ต้องการแบบละเอียดทุกขั้นตอนเลย เย้!

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 481

                    ทำยังไงให้เพิ่ม List รหัสง่ายๆ

                    ที่นี้ถ้าอยากให้ Dynamic มากขึ้น แทนที่เราจะพิมพ์ List ของรหัส EMS ที่ต้องการ Track เข้าไปเอง เราก็ดึง List นั้นมาจาก Table ของ Excel ได้ เช่น ผมสร้าง Table ชื่อ TrackTable แล้วมีคอลัมน์ TrackNumber เอาไว้ (เอาจริงๆ จะเป็น Data Source อะไรก็ได้อ่ะนะ)

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 482

                    จากนั้น Get Data เข้า Power Query ซะ

                    จากนั้นคลิ๊กขวาในคอลัมน์ที่ต้องการแล้ว DrillDown เราก็จะได้ List แล้ว

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 483

                    เนี่ยได้ List แล้ว ซึ่ง List นี้ชื่อว่า TrackTable

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 484

                    จากนั้นเราก็แค่เอา TrackTable ไปใส่ใน Code เดิมของเรา ก็จบ ง่ายขึ้นอีกเยอะ!!

                    let
                    
                        BarCodeList=TrackTable, //List เก็บรหัส Track
                        DataToken="xxxxxxx",  //Token รับส่งข้อมูลยาวๆ 
                        MyRecord=[status="all",language="TH",barcode=BarCodeList], //Record ที่จะ Post เข้า API
                    
                        Source = Json.Document(Web.Contents("https://trackapi.thailandpost.co.th/post/api/v1/track", 
                        [
                            Headers=
                                [
                                Authorization="Token "&DataToken,
                                #"Content-Type"="application/json"
                                ],
                            Content=Json.FromValue(MyRecord)                  
                        ] 
                    
                        ))
                    in
                        Source

                    เนี่ย ทุกอย่างยังทำงานได้เหมือนเดิม แต่เจ๋งกว่าเดิมตรงแก้รหัสที่จะ Track ได้ง่ายเลย!!

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 485

                    และนี่คือผลลัพธ์ที่เราได้ครับ แจ่มมากๆ

                    วิธีใช้ Power Query ดึงข้อมูล EMS Tracking จากไปรษณีย์ไทย ผ่าน Web API 486

                    ข้อควรระวัง ที่เขียนไว้ในระบบ คือ  สามารถใช้งาน API ได้สูงสุด 1,000 หมายเลขต่อการ Post 1 ครั้ง โดยสามารถใช้งานได้ 15 ครั้ง ต่อ 3 วินาที กรณีมีการใช้งานเกิน 15 ครั้ง ต่อ 3 วินาที ระบบจะทำการปิดกั้นการใช้งานชั่วคราว เป็นระยะเวลา 30 วินาที

                    ดังนั้นอย่าส่งเยอะและถี่เกินไปนะครับ ^^

                  • เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1

                    ถ้าใครได้หัดใช้ DAX ไม่ว่าจะใน Power BI หรือ Data Model ของ Excel มาซักพักจะรู้ว่า ฟังก์ชันที่ทรงพลังที่สุดใน DAX ก็คือ CALCULATE (และพี่น้องของมันอย่าง CALCULATETABLE ) อย่างไม่ต้องสงสัย

                    ในบทความนี้ผมจะมาทำความเข้าใจพฤติกรรมของ CALCULATE แบบเจาะลึกกันแบบละเอียดยิบๆๆๆๆ โดยทำการทดสอบหลายๆ อย่างพร้อมบันทึกภาพประกอบไว้ด้วย เพื่อให้ทุกท่านได้ร่วมพิสูจน์หาความจริงไปด้วยกัน

                    ป.ล. ตอนแรกจะทำตอนเดียวจบ แต่เขียนไปแล้วยาวมากๆ ยังไม่จบเลย ขอเอามาให้ดู 1 Part ก่อนแล้วกันครับ 555

                    ไฟล์ประกอบบทความ

                    https://github.com/ThepExcel/download/blob/master/DAX-Advance-start.pbix

                    เพื่อนๆ สามารถลองโหลดไฟล์นี้มาทำตามไปด้วยกันได้เลยครับ

                    ก่อนอื่นเรามาดูหน้าตา Data Model กันก่อน

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 487
                    • ตาราง Fact คือตารางชื่อ OrderDetail และตาราง target นอกนั้นเป็นตาราง Dimension ทั้งหมด
                    • ตาราง Dimension ที่ Filter ทั้ง OrderDetail และ target ได้ ก็คือตาราง dDate และ ProductCategory เท่านั้น
                    • ส่วนตาราง dDate ผูกกับ OrderDetail ผ่าน Relationship 2 เส้น คือ
                      • เส้น Active : Date -> Order Date
                      • เส้น Inactive : Date -> Delivery Date

                    Measure พื้นฐานที่จะใช้

                    ใช้แค่ SUM กับ DISTINCTCOUNT ง่ายๆ แบบนี้เลยครับ

                    หายอดจำนวนชิ้นรวม

                    Total Qty = SUM(OrderDetail[Quantity])

                    หายอดขายรวม

                    Total Revenue = SUM(OrderDetail[Revenue])

                    หาจำนวนลูกค้าที่ซื้อของ

                    Total Customer = DISTINCTCOUNT(OrderDetail[CustomerKey])

                    ทำความเข้าใจ Filter Context

                    CALCULATE และ CALCULATETABLE เป็นฟังก์ชัน DAX ที่มีความสามารถพิเศษคือ การเปลี่ยนแปลง Filter Context ได้ก่อนที่จำทำการคำนวณผลลัพธ์ออกมา ซึ่ง CALCULATE จะได้ผลลัพธ์เป็น Scalar (ค่าเดียว) แต่ CALCULATETABLE จะให้ผลลัพธ์เป็น Table (เป็นตาราง แต่จะมีกี่คอลัมน์กี่แถวก็ได้)

                    อะไรคือเปลี่ยนแปลง Filter Context ก่อนคำนวณผล? จะเข้าใจได้ เราก็ต้องรู้ก่อนว่า Filter Context คืออะไร

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 488

                    หากพิจารณาผลลัพธ์จาก Matrix ตัวนี้ที่ใช้ Measure Total Qty ในช่อง Value

                    ตัวเลขแต่ละตำแหน่งของตาราง Matrix ให้ผลลัพธ์ที่ไม่เหมือนกันได้ แม้จะใช้ Measure เดียวกัน เป็นเพราะมี Filter Context ที่ต่างกัน

                    ตัวเลขที่ผมกำลังเลือกดูนั้นอยู่ในจุดที่มี Filter Context คือ

                    • Product[Brand] เป็น Contoso
                      และ
                    • Customer[Customer Type] เป็น Person

                    นั่นแปลว่าก่อนที่ Model จะคำนวณค่า Total Qty ออกมาได้ มันจะต้องทำการ Filter Data Model ตามเงื่อนไขของ Filter Context ซะก่อน ทำให้ผลลัพธ์ของ OrderDetail[Quantity] ที่มองเห็นเหลือน้อยลงก่อนจะถูก SUM ออกมาโดย Measure นั่นเอง

                    ลองใช้ CALCULATE เปลี่ยน Filter Context

                    สมมติว่าผมไม่แบ่ง Customer Type ใน Matrix แต่ใช้การสร้าง Measure ใหม่ด้วย CALCULATE จะได้ผลแบบนี้

                    Person Cust Total Qty = CALCULATE([Total Qty],Customer[Customer Type]="Person")
                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 489

                    นั่นคือ ผมใช้ CALCULATE เปลี่ยนแปลง Filter Context ก่อนจะคำนวณ Total Qty ออกมา โดยสั่งให้ Customer Type เป็น Person ซะ ดังนั้นในแถวที่ Brand เป็น Contoso จะเห็นว่า

                    • Total Qty จะได้ 117,157 ซึ่งมีผลมาจาก Filter Context เรื่อง
                      • Product[Brand] เป็น Contoso อย่างเดียว
                      • แล้วคำนวณ SUM(OrderDetail[Quantity]) ทีหลัง
                    • Person Cust Total Qty จะได้ 16,241 เพราะมีผลมาจาก Filter Context เรื่อง
                      • Product[Brand] เป็น Contoso และ
                      • Customer[Customer Type] เป็น Person
                      • แล้วคำนวณ [Total Qty] ทีหลัง

                    ตรงนี้หลายคนอาจจะคิดว่า CALCULATE เป็นการสั่งให้ Filter เพิ่ม แต่จริงๆ แล้วมันคือการเปลี่ยน Filter ต่างหาก เช่น หากผมสั่งแบบนี้

                    Contoso Total Qty = CALCULATE([Total Qty],'Product'[Brand]="Contoso")
                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 490

                    จะเห็นว่าในบรรทัดที่ Brand ไม่ใช่ Contoso อย่างเช่น Litware ที่ควรมี Filter Context คือ Product[Brand] เป็น Litware ก็ได้ถูกเปลี่ยนให้กลายเป็น Product[Brand] เป็น Contoso ด้วยเช่นกัน

                    นอกจากที่เราจะเปลี่ยน Filter ได้แล้ว เรายังสามารถปลด Filter ทิ้งได้ด้วย โดยการใช้ REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT

                    Total Qty All Brand = CALCULATE([Total Qty],
                    REMOVEFILTERS('Product'[Brand])    //หรือ ALL('Product'[Brand]) ก็ได้
                    )

                    เช่น ถ้าเราใช้ REMOVEFILTERS ไปที่ ‘Product'[Brand] ก็จะแปลว่าเราปลด Filter เรื่อง Product Brand ออกซะ ดังนั้น Filter Context เดิมที่มีอยู่เกี่ยวกับ Brand ก็จะถูกทำลายทิ้ง ทำให้เราเราเห็นยอดของ Total Qty รวมของทุก Brand เสมอ (ไม่ว่าเดิมจะเคยมี Filter มาจากไหนก็ตาม)

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 491

                    ทำความเข้าใจ ALL เพิ่มซักนิด

                    REMOVEFILTERS กับ ALL นั้นถ้าใช้ในฐานะของ Filter ของ CALCULATE จะทำงานเหมือนกันเลย คือ การปลด Filter ทิ้ง แต่ว่า ALL (และผองเพื่อนคนอื่นๆ ) สามารถใช้ในฐานะ Table Function ได้ด้วย นั่นคือจะสร้างตารางออกมาแบบเห็นทุกค่าเหมือนไม่ได้มีการ Filter ใดๆ เช่น

                    ALL('Product'[Brand])
                    • ถ้าใช้ในฐานะ Filter ของ CALCULATE แบบตรงๆ : จะทำการ ปลด Filter จาก Field Brand ของตาราง Product (ปลด แปลว่าไม่มีระบุค่าใดๆ)
                    • ถ้าใช้ในฐานะ Table Function : เช่น ใช้ใน FILTER แปลว่าจะเป็น การได้ตารางที่แสดงค่าของคอลัมน์ที่เลือกออกมาแบบเห็นทุกค่าเสมอ เหมือนไม่ได้มีการ Filter ใดๆ (ไม่ว่าจริงๆจะมี Filter มาจากอะไรก็ตาม)

                    ดังนั้นถ้าเจอสูตรว่า ALL จึงต้องดูให้ดีด้วยว่าใช้ในฐานะอะไร ดังนั้นเพื่อไม่ให้สับสน การจะปลด Filter เฉยๆ เค้าเลยนิยมใช้ REMOVEFILTERS แทนเพราะชัดเจนกว่า

                    แล้วถ้าเราเขียนทุกอย่างปนกันล่ะ?

                    CALCULATE มีวิธีการจัดการ Filter พวกนี้ยังไง? แล้ว Filter Context เดิมล่ะ? แล้วยังมีการปลด Filter ได้อีก? ถ้าเขียนทุกอย่างผสมกันหมดเลยล่ะ จะเป็นไง?

                    การจะเข้าใจเรื่องพวกนี้ได้อย่างลึกซึง เราจะต้องแบ่งเรื่องของ Filter ออกเป็นกลุ่มๆ ดังนี้ก่อน

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                    2. Row Context ดั้งเดิม
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER

                    พอแบ่งเป็นกลุ่มได้แล้ว CALCULATE จะทำงานแบบนี้

                    • CALCULATE จะพิจารณา Filter Argument (ที่จริงๆ คือตาราง) ในกลุ่ม 3 (พิจารณาในสภาวะของ Filter Context และ Row Context เดิม) เก็บไว้ในใจก่อน แม้จะพิจารณาแต่แรก แต่จะเก็บเอาไว้ใช้ตอนหลังสุด
                    • CALCULATE จะ Copy Filter Context กลุ่ม 1 ออกมาสร้างเป็น New Filter Context
                    • CALCULATE จะ แปลง Row Context กลุ่ม 2 ให้กลายเป็นส่วนหนึ่งใน Filter ของ New Filter Context ผ่านกระบวนการ Context Transition
                    • CALCULATE จะทำตาม CALCULATE Modifier ในกลุ่ม 4 (เช่น ปลด Filter, เปลี่ยนเส้น Relationship)
                    • CALCULATE จะนำ Filter Argument ที่เก็บไว้ตั้งแต่ขั้นตอนแรกมาใช้ในตอนจบ (หลัง CALCULATE Modifier)
                      • ถ้ามีการพูดถึง Field ที่ไม่เคยมีใน New Filter Context ก็จะเป็นการ Add เงื่อนไขเพิ่ม
                      • ถ้ามีการพูดถึง Field ที่เคยมีใน New Filter Context จะแบ่งเป็น 2 กรณี
                        • ถ้าพูดถึงแบบปกติ จะเป็นการแทนค่าด้วยเงื่อนไขใหม่ทันที
                        • ถ้ามีการใช้ KEEPFILTERS ด้วย จะยังเก็บเงื่อนไขเดิมไว้ ไม่แทนค่าทับ (กลายเป็นเงื่อนไขแบบ AND)

                    ตัวอย่างการไล่ Step การทำงานของ CALCULATE

                    สมมติผมเขียน Measure ใหม่ดังนี้ แล้วมาทำความเข้าใจกันว่าเกิดอะไรขึ้น

                    Case Study 1 : CALCULATE 2 เงื่อนไข มีซ้ำ 1

                    CustPerson buy Contoso Total Qty = 
                    CALCULATE([Total Qty],
                    Customer[Customer Type]="Person",
                    'Product'[Brand]="Contoso")
                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 492

                    ในจุดที่ผมเอา Mouse ชี้ไว้ เรามาพิจารณากันว่ามีอะไรบ้าง

                    แบ่งกลุ่ม

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                      • ‘Product'[Brand] = “Fabrikam”
                    2. Row Context ดั้งเดิม
                      • ไม่มี
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                      • ‘Product'[Brand] = “Contoso”
                      • Customer[Customer Type] = “Person”
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER
                      • ไม่มี

                    ขั้นตอนการทำงาน

                    • เก็บ Filter Argument เอาไว้ในใจ
                      • ‘Product'[Brand] = “Contoso”
                      • Customer[Customer Type] = “Person”
                    • Copy Filter Context เดิมมาเป็น New Filter Context
                      • ‘Product'[Brand] = “Fabrikam”
                    • ไม่มี Row Context
                    • ไม่มี CALCULATE Modifier
                    • เอา Filter Argument ที่เก็บไว้มาใช้ สรุปแล้ว New Filter Context ได้แบบนี้
                      • ‘Product'[Brand] = “Fabrikam” จะถูกแทนด้วย ‘Product'[Brand] = “Contoso” (จาก Filter Argument )
                      • Customer[Customer Type] = “Person”

                    สรุป

                    เหลือเงื่อนไข 2 อัน คือ

                    • ‘Product'[Brand] = “Contoso”
                    • Customer[Customer Type] = “Person”

                    ตัวอย่างนี้อาจจะยังง่ายๆ อาจยังไม่เห็นความจำเป็นที่ต้องมาคิดละเอียด แต่มันคือการฝึกคิดให้เป็นระบบ เดี่ยวเราลองมาดูตัวอย่างที่ซับซ้อนขึ้นกันครับ

                    Case Study 2 : CALCULATE ซ้อนกัน

                    สมมติว่าเดิมที่ผมเขียน Measure เอาไว้แล้วแบบนี้ คือสั่งให้ Brand เป็น Contoso

                    Contoso Total Qty = CALCULATE([Total Qty],'Product'[Brand]="Contoso")

                    แล้วผมก็เขียน Measure อีกตัวเรียกใช้ Measure ตัวนั้น แต่ดันให้ Brand เป็น Litware

                    Litware Call Contoso Total Qty = 
                    CALCULATE([Contoso Total Qty],
                    'Product'[Brand]="Litware")

                    แบบนี้ผลลัพธ์จะเป็นยังไง? ในเมื่อสิ่งที่ต้องการมันดู Conflict กัน… แล้วมันจะเชื่อตัวไหน หรือมีวิธีคิดยังไง?

                    เรามาดูผลลัพธ์กันก่อนครับ ปรากฏว่าผลลัพธ์ออกมาเหมือน Filter เป็น Brand Contoso เลย… ทำไมล่ะ? แล้ว Litware ที่เราสั่งล่ะ??

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 493

                    ถ้าพิจารณาดีๆ แล้ว โปรแกรมจะมองสูตรของเราเป็น CALCULATE ซ้อนกันครับ ซึ่งมองเป็นแบบนี้ได้

                    Litware Call Contoso Total Qty = 
                    CALCULATE(
                        CALCULATE([Total Qty],
                        'Product'[Brand]="Contoso"),
                    'Product'[Brand]="Litware")

                    ซึ่งปกติแล้ว เวลาเขียนฟังก์ชันซ้อนกัน เราต้องคิดตัวในก่อนจริงมั้ยครับ? ซึ่งก็คิดตัวในก่อนเนี่ยถูกแล้ว แต่มันลึกซึ้งตรงที่ข้างในดันเป็น CALCULATE ที่มีนิสัยต้องพิจารณา Filter Context รอบนอกก่อนจะคำนวณนี่สิ

                    นั่นคือ CALCULATE ตัวใน จะพิจารณา Filter Context ที่มีผลจาก CALCULATE ตัวนอกด้วยนั่นเองดังนี้

                    สมมติว่าดูบรรทัด Fabrikam แล้วกันนะครับ เราจะพิจารณาว่าทำไมได้ 117,157

                    แบ่งกลุ่ม

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                      • ‘Product'[Brand] = “Fabrikam” (จาก Visual)
                      • ‘Product'[Brand] = “Litware” (จาก CALCULATE ตัวนอก ซึ่งก็คิดแล้วทับเงื่อนไข Fabrikam จาก Visual ไปเรียบร้อย…)
                    2. Row Context ดั้งเดิม
                      • ไม่มี
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                      • ‘Product'[Brand] = “Contoso”
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER
                      • ไม่มี

                    ขั้นตอนการทำงาน

                    • เก็บ Filter Argument เอาไว้ในใจ
                      • ‘Product'[Brand] = “Contoso”
                    • Copy Filter Context เดิมมาเป็น New Filter Context
                      • ‘Product'[Brand] = “Litware” (จาก CALCULATE ตัวนอก)
                    • ไม่มี Row Context
                    • ไม่มี CALCULATE Modifier
                    • เอา Filter Argument ที่เก็บไว้มาใช้ สรุปแล้ว New Filter Context ได้แบบนี้
                      • ‘Product'[Brand] = “Litware” (จาก CALCULATE ตัวนอก) เดิม จะถูกแทนด้วย ‘Product'[Brand] = “Contoso” (จาก Filter Argument )

                    สรุป

                    เหลือเงื่อนไข 1 อัน คือ

                    • ‘Product'[Brand] = “Contoso”

                    Case Study 3 : เขียน 2 เงื่อนไข Conflict กันตรงๆ

                    สมมติผมดันเขียนเงื่อนไขที่ Conflict กันเอง ใน CALCULATE เดียวกันบ้าง จะเป็นยังไง?

                    Contoso Litware Total Qty = 
                    CALCULATE([Total Qty],
                    'Product'[Brand]="Contoso",
                    'Product'[Brand]="Litware")

                    เราเขียนให้ Brand เป็น Contoso และก็ Brand เป็น Litware ซึ่งเห็นผลลัพธ์เลยว่าได้ค่าออกมาเป็น Blank นั่นคือมันไม่มีค่าแบบนี้นั่นเอง

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 494

                    ทั้งนี้เพราะในขั้นตอนสุดท้ายของ CALCULATE มันเอาเงื่อนไข 2 อันนี้ มาเชื่อมกันแบบ AND มันก็เลยไม่มีผลลัพธ์ตารางใดๆ ออกมาเลยนั่นเอง

                    • ‘Product'[Brand]=”Contoso”
                    • ‘Product'[Brand]=”Litware”

                    ผลลัพธ์สุดท้ายเลยได้ Blank ไปโดยปริยาย

                    Case Study 4 : การใช้ USERELATIONSHIP

                    สมมติว่าผมสามารถคำนวณ Total Qty โดย แบ่งตาม Year และ Month ใน Date Table แบบนี้เราสามารถตีความได้ว่าเป็น Total Qty ของแต่ละเดือน/ปี ที่ Order สินค้า ทั้งๆ ที่ไม่ได้เขียนระบุไว้ที่ไหนเลย

                    สาเหตุที่เป็นแบบนี้เพราะเส้น Relationship ที่ Active อยู่คือเส้นที่เชื่อมระหว่าง Date ของ Date Table -> Order Date ใน OrderDetail นั่นเอง

                    แต่ถ้าหากผมต้องการสร้าง Measure อีกตัวเป็นการคำนวณ Total Qty โดยแบ่งตาม แต่ละเดือน/ปี ที่ Deliver สินค้า ผมจะต้องเขียน Measure ดังนี้

                    Total Qty Delivery = CALCULATE([Total Qty],
                    USERELATIONSHIP(dDate[Date],OrderDetail[DeliveryDate]))
                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 495

                    เราจะได้เลข Total Qty ที่ยึดจาก Delivery Date ออกมาในเฉพาะ Measure นี้โดยไม่ต้องไปแก้ที่ Data Model เลย (การแก้ Data Model จะกระทบต่อการ Filter Date ทั้งหมด ซึ่งอาจจะไม่ใช่สิ่งที่เราต้องการ)

                    สมมติว่าผมอยู่ที่เดือน March ของ 2019 นะครับ เรามาดูว่าทำไมผลลัพธ์ถึง Work?

                    แบ่งกลุ่ม

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                      • dDate[Year]=2019
                      • dDate[Month Name]=”March”
                    2. Row Context ดั้งเดิม
                      • ไม่มี
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                      • ไม่มี
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER
                      • USERELATIONSHIP สั่งให้เปลี่ยนเส้นเป็น Delivery Date

                    ขั้นตอนการทำงาน

                    • เก็บ Filter Argument เอาไว้ในใจ
                      • ไม่มี
                    • Copy Filter Context เดิมมาเป็น New Filter Context
                      • dDate[Year]=2019
                      • dDate[Month Name]=”March”
                    • ไม่มี Row Context
                    • CALCULATE Modifier
                      • USERELATIONSHIP สั่งให้เปลี่ยนเส้นเป็น Delivery Date
                    • สรุปแล้ว New Filter Context ได้แบบนี้
                      • dDate[Year]=2019
                      • dDate[Month Name]=”March”
                      • แต่ต่างจาก Context ดั้งเดิมตรงที่เรามีการเปลี่ยนเส้น Relationship แล้วใน Step ก่อนหน้านี้ มันจึงกลายเป็น Year และ Month Name ที่ Deliver ของนั่นเอง

                    จริงๆ แล้วเงื่อนไขใน Filter ของ CALCULATE คือ Table

                    การที่เราเขียนเงื่อนไขใน Filter ของ CALCULATE เช่น

                    Contoso Total Qty = CALCULATE([Total Qty],
                    'Product'[Brand]="Contoso")

                    นั้นจริงๆ แล้วมันคือตัวย่อของการเขียนแบบนี้ (โปรแกรมมันจะแปลงสูตรข้างบนให้กลายเป็นแบบนี้ก่อนจะลงมือคำนวณ ดังนั้นจึงไม่ต่างกันด้าน performance เลย)

                    Contoso Total Qty Full = CALCULATE([Total Qty],
                    FILTER(ALL('Product'[Brand]),'Product'[Brand]="Contoso") )
                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 496

                    หากเรามาคิดว่าการเขียนว่า

                    FILTER(ALL('Product'[Brand]),'Product'[Brand]="Contoso")

                    แบบนี้มันจะได้ผลเป็นยังไง?

                    • ALL(‘Product'[Brand]) จะสร้างตารางรายชื่อ Brand ที่มีครบทุก Brand เสมอ
                    • แล้ว FILTER ก็ใส่เงื่อนไขให้ตารางนั้นเหลือแค่ Contoso ค่าเดียว

                    เราจะพบว่ามันก็จะได้ ผลเป็นตาราง ที่มี 1 คอลัมน์คือ ‘Product'[Brand] และมี 1 ค่า คือ Contoso นั่นเอง (ไม่ว่าจะอยู่จุดไหนของรายงาน) ดังนั้นเราก็เลยเห้นทุก Brand ได้ค่าเท่ากับ Contoso หมดเลย

                    ซึ่งหากเราเปลี่ยนมุมมองเรื่องของ Filter ของ CALCULATE จากการมองว่า Filter มันเป็นแค่ “เงื่อนไข” ให้มองเป็น “ตาราง” แทน ทุกอย่างจะเป็นไปได้มากขึ้นอีกเยอะมาก ซึ่งจะได้เห็นใน Case Study ต่อจากนี้นะครับ

                    Case Study 5 : การคิด % เทียบกับ Product Category ใหญ่ แบบยืดหยุ่น

                    สมมติว่าผมต้องการคิดว่ายอด Total Qty ที่มีคิดเป็นกี่ % ของ Category ใหญ่ ผมจะต้องคำนวณยอด Category ใหญ่(ที่เป็นตัวหาร) ให้ถูกต้องให้ได้ก่อน เช่น ถ้า Matrix เป็นแบบนี้ ผมอาจจะใช้การ REMOVEFILTERS ที่ตัวย่อยมาช่วย

                    Total Qty All Subcat = CALCULATE([Total Qty],
                    REMOVEFILTERS(ProductCategory[Subcategory])) 
                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 497

                    แต่การทำแบบนี้มีข้อเสียคือ ถ้าผมเปลี่ยนตัวย่อยเป็นตัวอื่น Measure นั้นก็จะไม่ Work หรือถ้าผมไม่ใส่ตัว Product Category เข้าไปด้วยมันก็จะไม่ Work อยู่ดี เช่นแบบนี้ เราจะได้ยอด 327,044 ซึ่งเป็นยอดรวมทั้งหมดมาแทน ไม่ใช่ยอดของ ProductCategory นั้นๆ

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 498

                    ดังนั้นวิธีการจัดการ เราจะต้องใช้ความเข้าใจที่ลึกซึ้งขึ้น ว่าจริงๆ เงื่อนไข Filter ของ CALCULATE คือ Table ดังนั้นผมจะสามารถเขียนแบบนี้ได้ ซึ่งผมจะได้ผลลัพธ์เป็นยอดของ Category นั้นๆ เสมอ ไม่ว่าผมจะใส่ Product Category เข้าไปด้วยหรือไม่ก็ตาม

                    Total Qty Category Level = CALCULATE([Total Qty],
                    REMOVEFILTERS('Product'),
                    DISTINCT(ProductCategory[Product Category]))

                    ปล. ถ้าข้อมูลสมบูรณ์ดี ใช้ DISTINCT(ProductCategory[Product Category]) หรือ VALUES(ProductCategory[Product Category]) ก็ได้

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 499

                    ทีนี้เรามาทำความเข้าใจกันว่าทำไมสูตรนี้จึง Work โดยไล่ที่ละขั้นตอนตามที่ผมบอกไป สมมติว่าดูที่ Desktops

                    แบ่งกลุ่ม

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                      • ProductCategory[Subcategory]=”Desktops”
                    2. Row Context ดั้งเดิม
                      • ไม่มี
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                      • DISTINCT(ProductCategory[Product Category])
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER
                      • REMOVEFILTERS(‘Product’)

                    ขั้นตอนการทำงาน

                    • เก็บ Filter Argument เอาไว้ในใจ
                      • DISTINCT(ProductCategory[Product Category])
                        • ซึ่งจะมองเห็น Product Category ภายใต้ Filter Context เดิมนั่นคือจะเห็นแค่ Computers เท่านั้น
                        • จึงเปรียบเสมือนว่าเราได้เงื่อนไขออกมาว่า ProductCategory[Product Category]=”Computers” นั่นเอง
                    • Copy Filter Context เดิมมาเป็น New Filter Context
                      • ProductCategory[Subcategory]=”Desktops”
                    • ไม่มี Row Context
                    • CALCULATE Modifier
                      • REMOVEFILTERS(‘Product’) ส่งผลให้ปลด Filter ทั้งหมดของ Expanded Table จากตาราง Product (ตาราง Product รวมถึงทุกตารางที่เชื่อมกับมันที่เป็นฝั่งเลข 1) นั่นคือจะปลดทั้งตาราง Product และ ตาราง Product Category ไปด้วย
                      • ดังนั้น New Filter Context จึงหายไปหมด
                    • เอา Filter Argument ที่เก็บไว้มาใช้ สรุปแล้ว New Filter Context ได้แบบนี้
                      • ProductCategory[Product Category]=”Computers”

                    นั่นคือสรุปแล้ว มี 1 เงื่อนไขซึ่งเป็น Category ใหญ่ของสิ่งที่เรากำลังดูอยู่นั่นเอง

                    Case Study 6 : Time Intelligence ทำงานยังไงกันแน่?

                    สมมติว่าผมต้องการใช้ฟังก์ชัน Time Intelligence เช่น พวก YTD อย่าง TOTALYTD หรือตัวเต็มของมันคือ CALCULATE+DATESYTD จะได้แบบนี้

                    Total Qty YTD = CALCULATE([Total Qty],DATESYTD(dDate[Date]))
                    Total Customer YTD = CALCULATE([Total Customer],DATESYTD(dDate[Date]))

                    หากคิดแบบผิวเผินหลายคนจะคิดว่าการใช้ YTD คือการเอาค่ามาสะสมกันตั้งแต่ต้นปี แต่ในความเป็นจริงๆ มันไม่ใช่การสะสมค่า แต่เป็นการ “เปลี่ยนช่วงเวลาใน Filter Context” ให้กลายเป็นช่วงใหม่ต่างหาก

                    สังเกตได้ชัดจาก Total Customer YTD ในเดือน April 2019 ซึ่งไม่ใช่การเอาจำนวนลูกค้าใน 4 เดือนมารวมกัน แต่เป็นการคิดจำนวนลูกค้าในช่วง 4 เดือนแบบไม่ซ้ำต่างหาก

                    ที่เจ้า Total Qty YTD มันดูเหมือนเป็นการสะสมค่าตั้งแต่ต้นปี เป็นเพราะบังเอิญที่เราใช้ Measure ที่คำนวณด้วยการ SUM ต่างหาก

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 500

                    อีกเรื่องที่น่าสนใจคือช่วงเวลาที่มันคิด YTD คือช่วงเวลาไหน หลายคนก็จะไม่รู้ตรงนี้ เช่นตรงบรรทัด Total สุดท้าย ที่ไม่มี Filter เรื่องวันที่เลย ทำไมถึงได้เป็น 5207 เท่ากับปี 2021 เลย?

                    สาเหตุเป็นเพราะ YTD จะไปสร้างช่วงวันที่ Date ใหม่ขึ้นมา โดยยึดวันสุดท้ายใน Filter Context เป็นหลักก่อน แล้วเปลี่ยนจุดเริ่มต้นให้เป็นต้นปีเดียวกับวันสุดท้ายนั้นๆ นั่นเอง เรื่องพวกนี้เราก็ต้องไปทำความเข้าใจความสามารถของฟังก์ชัน Time Intelligence แต่ละตัวอีกที เพราะความสามารถในการสร้างช่วงวันที่ของมันไม่เหมือนกัน

                    แต่เรื่องที่น่าสนใจก็คือ หากคิดตามหลักการที่ผมสอนแล้ว ฟังก์ชัน Time Intelligence มันไม่น่าจะทำงานได้ถูกต้องนี่นา เราลองมาไล่ Step กัน สมมติว่าดูที่ April 2019

                    แบ่งกลุ่ม

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                      • dDate[Year]=2019
                      • dDate[Month Name]=”April”
                    2. Row Context ดั้งเดิม
                      • ไม่มี
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                      • dDate[Date] อยู่ในช่วง 1/1/2019 – 30/4/2019 (อันนี้คือตารางที่เกิดจาก Time Intelligence DATESYTD)
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER
                      • ไม่มี

                    ขั้นตอนการทำงาน

                    • เก็บ Filter Argument เอาไว้ในใจ
                      • dDate[Date] อยู่ในช่วง 1/1/2019 – 30/4/2019 (ตารางที่เกิดจาก Time Intelligence DATESYTD)
                    • Copy Filter Context เดิมมาเป็น New Filter Context
                      • dDate[Year]=2019
                      • dDate[Month Name]=”April”
                    • ไม่มี Row Context
                    • ไม่มีCALCULATE Modifier
                    • เอา Filter Argument ที่เก็บไว้มาใช้ สรุปแล้ว New Filter Context ได้แบบนี้
                      • dDate[Year]=2019
                      • dDate[Month Name]=”April”
                      • dDate[Date] อยู่ในช่วง 1/1/2019 – 30/4/2019

                    ถ้าเรามองแบบนี้ ผลลัพธ์จะยังต้องเป็นช่วงเวลา April 2019 อยู่ดี เพราะทุกเงื่อนไขต้องเอามา AND กันหมด (เนื่องจากพูดถึงคนละ Field กัน) แล้วทำไมผลลัพธ์มันถึงกลายเป็น ช่วง 1/1/2019 – 30/4/2019 ไปได้ล่ะ?

                    สาเหตุก็คือ เวลาที่เราเรียกใช้ฟังก์ชัน Time Intelligence หรือมีการอ้างอิงเงื่อนไขใน CALCULATE ไปที่คอลัมน์ Date ของ Date Table (โดยมีการ Mark as Date Table หรือ Relationship เชื่อมที่วันที่) โปรแกรมจะแอบปลด Filter ทั้ง Date Table ให้เราโดยอัตโนมัติ ดังนั้นจริงๆ แล้วผลการวิเคราะห์จะกลายเป็นแบบนี้

                    แบ่งกลุ่ม

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                      • dDate[Year]=2019
                      • dDate[Month Name]=”April”
                    2. Row Context ดั้งเดิม
                      • ไม่มี
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                      • dDate[Date] อยู่ในช่วง 1/1/2019 – 30/4/2019 (อันนี้คือตารางที่เกิดจาก Time Intelligence DATESYTD)
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER
                      • REMOVEFILTERS(dDate)

                    ขั้นตอนการทำงาน

                    • เก็บ Filter Argument เอาไว้ในใจ
                      • dDate[Date] อยู่ในช่วง 1/1/2019 – 30/4/2019 (ตารางที่เกิดจาก Time Intelligence DATESYTD)
                    • Copy Filter Context เดิมมาเป็น New Filter Context
                      • dDate[Year]=2019
                      • dDate[Month Name]=”April”
                    • ไม่มี Row Context
                    • CALCULATE Modifier
                      • REMOVEFILTERS(dDate)
                        ซึ่งจะปลดเงื่อนไข 2 อันนี้ออกไปหมด
                        • dDate[Year]=2019 และ
                        • dDate[Month Name]=”April”
                    • เอา Filter Argument ที่เก็บไว้มาใช้ สรุปแล้ว New Filter Context ได้แบบนี้
                      • dDate[Date] อยู่ในช่วง 1/1/2019 – 30/4/2019

                    Case Study 7 : YTD แบบนับเฉพาะวันจันทร์

                    หลังจากที่เราเข้าใจ Time Intelligence มากขึ้นแล้ว ว่ามันปลด Filter ทั้ง Date Table ให้เราด้วย เราลองมาทำความเข้าใจต่อว่าจะเกิดอะไรขึ้น ถ้าเราอยากคิดยอดสินค้าสะสมตั้งแต่ต้นปี แต่นับเฉพาะวันจันทร์…

                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 501

                    เราจะพบว่าผลลัพธ์ของ YTD นั้นดูไม่ใช่การสะสมค่าเฉพาะวันจันทร์อย่างแน่นอน เพราะเลขมันเยอะมากๆ เลยเมื่อเทียบกับ Total Qty เฉพาะวันจันทร์

                    ก่อนจะแก้ไขปัญหาได้ เราจะต้องเข้าใจปัญหาซะก่อน ว่ามันเกิดอะไรขึ้น? ดังนั้นเราลองมาวิเคราะห์กันว่าทำไมเดือน April 2019 ถึงได้ 38,511? (ซึ่งเยอะมากๆ แต่ก็ยังน้อยกว่า YTD อันเดิมที่ยังไม่ Filter วันจันทร์ แล้วได้ 38,822)

                    ถ้ามาไล่ดูว่ามันทำงานยังไง จะเป็นแบบนี้

                    แบ่งกลุ่ม

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                      • dDate[Year]=2019
                      • dDate[Month Name]=”April”
                      • dDate[DayName]=”Monday”
                    2. Row Context ดั้งเดิม
                      • ไม่มี
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                      • dDate[Date] อยู่ในช่วง 1/1/2019 – จันทร์สุดท้ายของเดือน April (อันนี้คือตารางที่เกิดจาก Time Intelligence DATESYTD)
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER
                      • REMOVEFILTERS(dDate)

                    ขั้นตอนการทำงาน

                    • เก็บ Filter Argument เอาไว้ในใจ
                      • dDate[Date] อยู่ในช่วง 1/1/2019 – จันทร์สุดท้ายของเดือน April (ตารางที่เกิดจาก Time Intelligence DATESYTD)
                    • Copy Filter Context เดิมมาเป็น New Filter Context
                      • dDate[Year]=2019
                      • dDate[Month Name]=”April”
                      • dDate[DayName]=”Monday”
                    • ไม่มี Row Context
                    • CALCULATE Modifier
                      • REMOVEFILTERS(dDate)
                        ซึ่งจะปลดเงื่อนไข 3 อันนี้ออกไปหมด
                        • dDate[Year]=2019 และ
                        • dDate[Month Name]=”April”
                        • dDate[DayName]=”Monday”
                    • เอา Filter Argument ที่เก็บไว้มาใช้ สรุปแล้ว New Filter Context ได้แบบนี้
                      • dDate[Date] อยู่ในช่วง 1/1/2019 – จันทร์สุดท้ายของเดือน April

                    จะเห็นว่าผลลัพธ์ไม่ได้เหลือแค่วันจันทร์ ได้แต่ข้อมูลตั้งแต่ต้นปีถึงวันจันทร์สุดท้ายที่มองเห็นต่างหาก สาเหตุเพราะมันถูกปลดเงื่อนไขไปจากการปลด Filter Date ทั้งตารางแบบอัตโนมัตินั่นเอง

                    ดังนั้นทางแก้ไขคือการใส่ Filter เรื่องวันเข้าไปใหม่ด้วย DISTINCT(dDate[DayName]) ก็ได้ ดังนี้

                    Total Qty YTD OnlySelectDayname = CALCULATE([Total Qty],
                    DATESYTD(dDate[Date]),DISTINCT(dDate[DayName]))
                    เจาะลึก CALCULATE ใน DAX แบบลึกสุดใจ : Part 1 502

                    แบ่งกลุ่ม

                    1. Filter Context ดั้งเดิมของจุดที่สนใจ (รวมถึง Filter Context ของ CALCULATE ชั้นนอกด้วย)
                      • dDate[Year]=2019
                      • dDate[Month Name]=”April”
                      • dDate[DayName]=”Monday”
                    2. Row Context ดั้งเดิม
                      • ไม่มี
                    3. เงื่อนไข Filter Argument (ที่จริงๆ คือตาราง) ที่ตั้งใจเขียนใน CALCULATE ตัวนั้นๆ
                      • dDate[Date] อยู่ในช่วง 1/1/2019 – จันทร์สุดท้ายของเดือน April (อันนี้คือตารางที่เกิดจาก Time Intelligence DATESYTD)
                      • DISTINCT(dDate[DayName]) ซึ่งจะได้วันที่เลือกมาคือวันจันทร์ เหมือนกับได้ว่า dDate[DayName]=”Monday”
                    4. CALCULATE Modifier เช่น REMOVEFILTERS, ALL, ALLSELECTED, ALLEXCEPT, USERELATIONSHIP, CROSSFILTER
                      • REMOVEFILTERS(dDate)

                    ขั้นตอนการทำงาน

                    • เก็บ Filter Argument เอาไว้ในใจ
                      • dDate[Date] อยู่ในช่วง 1/1/2019 – จันทร์สุดท้ายของเดือน April (ตารางที่เกิดจาก Time Intelligence DATESYTD)
                      • DISTINCT(dDate[DayName]) ซึ่งจะได้วันที่เลือกมาคือวันจันทร์ เหมือนกับได้ว่า dDate[DayName]=”Monday”
                    • Copy Filter Context เดิมมาเป็น New Filter Context
                      • dDate[Year]=2019
                      • dDate[Month Name]=”April”
                      • dDate[DayName]=”Monday”
                    • ไม่มี Row Context
                    • CALCULATE Modifier
                      • REMOVEFILTERS(dDate)
                        ซึ่งจะปลดเงื่อนไข 3 อันนี้ออกไปหมด
                        • dDate[Year]=2019 และ
                        • dDate[Month Name]=”April”
                        • dDate[DayName]=”Monday”
                    • เอา Filter Argument ที่เก็บไว้มาใช้ สรุปแล้ว New Filter Context ได้แบบนี้
                      • dDate[Date] อยู่ในช่วง 1/1/2019 – จันทร์สุดท้ายของเดือน April
                      • DISTINCT(dDate[DayName]) ซึ่งจะได้วันที่เลือกมาคือวันจันทร์ เหมือนกับได้ว่า dDate[DayName]=”Monday”

                    ดังนั้นผลลัพธที่ได้จึงเป็นช่วงต้นปีถึงจันทร์สุดท้ายของเดือนเมษา “และ” เพิ่มเงื่อนไขว่าต้องเป็นวันจันทร์ด้วย ดังนั้น ผลลัพธ์จะเหลือแค่วันจันทร์เท่านั้นนั่นเอง

                    ต่อตอนต่อไป

                    ตอนต่อไปจะมีเรื่องที่ซับซ้อนมากขึ้นอีกนั่นคือ Context Transition และเรื่องของการสร้างตารางด้วย CALCULATETABLE มาประกอบด้วยครับ

                  • Series สอนดึงข้อมูลจากเว็บ ด้วย Power Automate Desktop

                    Series สอนดึงข้อมูลจากเว็บ ด้วย Power Automate Desktop

                    ในคลิปนี้ผมจะสอนใช้เครื่องมือ Power Automate Desktop (โหลดมาใช้ได้ฟรี) เพื่อดึงข้อมูลจากเว็บไซต์ที่ต้องการมาลงใน Excel แบบอัตโนมัติ

                    ซึ่งดึงได้หมดแม้ว่าเว็บนั้นจะต้อง Login ก่อน หรือมีหลายหน้าก็ไม่มีปัญหา แต่เราจะสอนตั้งแต่แบบง่ายไปยาก ดังนั้นในคลิปนี้จะเป็นการดึงแบบง่ายก่อนครับ ดังนั้นใครอยากทำกับเว็บยากๆ ได้ให้ติดตามยาวๆ เลยครับ ^^

                    ตอนที่ 1 : เริ่มดึงจากเว็บหน้าแรก

                    ตอนที่ 2 : ดึงข้อมูลหลายๆ หน้า มารวมกัน

                  • ทำความเข้าใจวิธีสั่ง MidJourney แบบละเอียด เพื่อให้ได้ผลลัพธ์ตรงใจ (มากขึ้น)

                    ทำความเข้าใจวิธีสั่ง MidJourney แบบละเอียด เพื่อให้ได้ผลลัพธ์ตรงใจ (มากขึ้น)

                    บทความนี้จะเป็น “การทดลองทำความเข้าใจ” AI วาดรูปที่ชื่อว่า MidJourney (ใช้งานผ่าน Discord) โดยเป็นสิ่งที่ผมได้เรียนรู้มาจากการอ่านและการทดลองทำด้วยตัวเองนะครับ หวังว่าจะมีประโยชน์กับคนที่กำลังเรียนรู้การสั่งการให้ตรงใจมากขึ้นนะ

                    ในบทความนี้จะไม่มีการสอนการเรียกใช้งานทั่วไปซึ่งตอนนี้เรื่องพวกนี้น่าจะสามารถหาดู Tutorial ได้ง่ายๆ แล้ว แต่บทความนี้จะเน้นไปที่การเจาะลึกไปที่ Prompts หรือตัวคำสั่งที่เราสั่งหลังจาก /imagine นี่แหละ

                    ข้อมูลเบื้องต้นที่ควรศึกษาก่อน

                    แต่ถ้าจะให้แนะนำคลิปก็ขอแนะนำอันนี้ครับ อธิบายภาพรวมได้ดี (ภาษาอังกฤษ)
                    คลิปภาษาไทยก็มี เผื่อคนอังกฤษไม่แข็งแรง

                    เบื้องต้น ผมแนะนำให้เพื่อนๆ ที่ยังไม่เคยใช้งานไปหัดใช้งานเบื้องต้นก่อนนะครับ เพราะผมจะไม่ได้พูดถึงเรื่องพื้นฐานทั่วไปในบทความนี้นะ ใครอยากรู้จัก Parameter ต่างๆ ว่าส่งผลยังไงให้ไปอ่าน บทความหลักอันนี้ในเว็บของ MidJourney อันนี้ได้เลย มีรูปประกอบดีอยู่แล้ว

                    สรุปองค์ประกอบของ Prompt ที่ใช้สั่งงาน

                    ซึ่งถ้าไปอ่านจะพบว่าโครงสร้างหลักของ Prompts จะมีอยู่ 3 ส่วนดังรูปนี้

                    ทำความเข้าใจวิธีสั่ง MidJourney แบบละเอียด เพื่อให้ได้ผลลัพธ์ตรงใจ (มากขึ้น) 503
                    • ส่วนแรก คือ Image Prompt สามารถใช้เป็น “แรงบันดาลใจ” ให้กับ MidJourney ได้
                      • ซึ่งเราสามารถใส่เป็น URL (ที่อยู่เว็บ) ที่ Link ไปหารูป (.png หรือ .jpg)
                      • โดยสามารถใส่หลายรูปก็ได้ด้วยนะครับ แค่เว้นวรรคแล้วใส่ URL ไปเรื่อยๆ
                      • ถ้ามีแต่รูปในเครื่องคอมพ์ ก็ให้ Upload ขึ้น Discord ก่อนแล้วคลิ๊กขวา copy link เอา URL มาก็ได้
                      • อย่างไรก็ตาม มันไม่ได้เอารูปมาสร้างต่อตรงๆ แต่เป็น “แรงบันดาลใจ” แค่ส่วนหนึ่งของคำสั่งรวมกับ Text Prompts เท่านั้น
                        • ปกติ Image จะมี Weight ที่ 0.25 เมื่อเทียบกับ Text Prompt ปกติที่มี Weight 1 นั่นคือ Image Prompt จะส่งผลที่ 20% เท่านั้น แต่เราสามารถปรับ Image Weights ได้ ด้วย parameter –iw number นะครับ
                    • ส่วนที่สอง คือ Text Prompt ตรงนี้สามารถใส่คำบรรยายต่างๆ ได้มากที่สุด 60 คำ และไม่เกิน 6000 อักขระ ตัวพิมพ์เล็กพิมพ์ใหญ่ไม่มีผลใดๆ ทั้งสิ้น ซึ่งในส่วนนี้เราสามารถใส่ตัวคั่นต่างๆ ได้มากมายหลายแบบด้วย เช่น , + :: ซึ่งผมจะพูดในรายละเอียดต่อไปอีกที เพราะส่วนนี้แหละที่มีรายละเอียดเยอะมาก โดยเฉพาะการกำหนด Weights หรือน้ำหนักให้กับคำสั่งแต่ละชุด
                    • ส่วนสุดท้าย คือ Parameters พวกนี้จะขึ้นต้นด้วยเครื่องหมาย — ซึ่งมีหลายคำสั่งมากๆ ซึ่งอันนี้ไม่ใช่ List ที่ครบทั้งหมด แต่ผมเอามาแค่ที่ใช้ให้ดูในบทความนี้นะครับ ถ้าอยากรู้ทั้งหมดให้ไปอ่านในบทความหลักอันนี้ในเว็บของ MidJourney เองนี้เอานะครับ
                      • –stop number ให้หยุดการ gen รูปที่ percent ที่กำหนด (เพื่อที่จะได้รูปที่ไม่ละเอียดเกินไป)
                      • –seed number กำหนดเลข seed ที่ใช้ในการ Random รูปออกมา ซึ่งจะทำให้การ Gen แต่ละรอบ ค่อนข้างได้รูปที่ไม่ต่างจากเดิมมาก
                      • –sameseed number กำหนดเลข seed ตอนที่สร้างรูป 4 รูป ให้เป้นอันเดียวกัน ดังนั้นรูป 4 รูปจะออกมาคล้ายกันมากๆ
                      • –ar h:w กำหนดสัดส่วน Aspect Ratio ของรูป (ใช้แทน –w กับ –h ได้)
                      • –no xxx yyy คือการกำหนดให้ในรูปไม่มีสิ่งที่ระบุ (ทำให้รูปตรงใจมากขึ้น แต่ว่าใช้กับคำต้องห้ามไม่ได้ เช่น –no dress ไม่ได้)
                      • –iw number (ถ้าไม่กำหนด สัดส่วนของ image prompt จะอยู่ที่ 0.25 หรือ 25%)
                      • –video คือ สั่งให้มันบันทึกการ Gen รูปเป็น Video ไว้ด้วย และถ้าเราส่ง Emoji จดหมายหามัน มันจะส่งคลิปวีดีโอกลับมาให้

                    เทคนิคพิมพ์ Parameter สำหรับ iPhone

                    ใน iPhone ถ้าเราพิมพ์ — (ขีด 2 ทีติดกัน) บางทีมันมักจะแปลงให้เป็น ขีดเดียวยาวๆ ให้โดยอัตโนมัติ (ซึ่งใช้ไม่ได้) ให้เราไปปรับค่าใน Setting –> General –> Keyboards –> ปิด Smart Punctuations ซะ จะสะดวกขึ้นมากๆ

                    ทำความเข้าใจวิธีสั่ง MidJourney แบบละเอียด เพื่อให้ได้ผลลัพธ์ตรงใจ (มากขึ้น) 504

                    ทำไมสั่งด้วย Prompts ที่เหมือนกัน แต่ให้ผลลัพธ์ไม่เหมือนกัน?

                    เพื่อนๆ งงมั๊ยว่า สมมติเราไปรู้ Prompt เจ๋งๆ ของคนอื่นมา เช่น ผมไปแอบดูรูปที่คนอื่นทำใน Community Feed (ที่รวมผลงานเจ๋งๆ แต่เราต้องเป็นสมาชิกเสียเงินถึงจะดูได้นะ ) แล้วชอบ ก็เลยจะจิ๊ก Prompt หรือแม้แต่ Command ที่เค้าใช้มาลองบ้าง

                    เช่น ผมชอบรูปนี้ (จาก Link นี้) เลย Copy Command โดยกด … แล้ว Copy Command มาแบบนี้

                    ทำความเข้าใจวิธีสั่ง MidJourney แบบละเอียด เพื่อให้ได้ผลลัพธ์ตรงใจ (มากขึ้น) 505

                    แล้วได้คำสั่งนี้มา

                    sui ishida art manga, female Samurai, Symatrical, dark atmospheric lighting, volumetrics, manga style, artstation, symetric, lineart --ar 9:16

                    แต่พอเอาไปสั่งเอง ได้รูปอะไรไม่รู้ ไม่เห็นจะมีความคล้ายรูปนั้นเลย? แถมลองรันตั้งหลายรอบก็ยังไม่ได้ (แถมไม่เหมือนกันซักรอบ)

                    ค่อยๆ ทำความเข้าใจ

                    ผมจะอธิบายหลักการทำงานของมันให้ฟัง ว่าทำไมเราถึงไม่ได้รูปเหมือนของเค้า (ที่เราก๊อปมา) ขอเอาตัวอย่างที่ Simple มากๆ ก่อน เช่น สีแดงกับเหลือง

                    สมมติว่าผมส่ง MidJourney ด้วย Promt ง่ายๆ เช่น

                    red , yellow

                    แต่ผลลัพธ์แต่ละครั้งที่มัน Gen จะไม่เหมือนกัน เช่น จะได้ 2 ชุดนี้ออกมา

                    ที่เราได้ผลลัพธ์ไม่เหมือนกัน เป็นเพราะการ Gen แต่ละครั้งนั้นมีจุดเริ่มต้นของการ Random ที่เรียกว่า “seed” ที่แตกต่างกัน

                    ถ้าใครอ่านวิธีการสั่ง parameter จะพบว่าเราสามารถกำหนดเลข seed ของการ Gen ได้ สมมติผมสั่งให้เป็น –seed 1 ทั้งสองรอบ เราจะได้รูปที่คล้ายกันขึ้นมาก ดังนี้

                    red, yellow --seed 1

                    เราจะเห็นได้ว่าลักษณะการวางสีแทบจะเหมือนกัน มันต่างกันที่รายละเอียด ดังนั้นถ้าเราสั่งให้หยุดการ Gen ไว้ก่อนจบ จะเห็นได้ชัดเลยว่าตอนแรกมัน Random ออกมาเหมือนกัน เช่น อันนี้ผมสั่งหยุดที่ 20% จะพบว่าการ Gen 2 รอบคล้ายกันมากๆ

                    red, yellow --seed 1 --stop 20

                    ซึ่งถ้าดูเป็นภาพเคลื่อนไหว โดยใส่ –video แล้วสุดท้ายให้ dm หา bot มันจะส่งวีดีโอกลับมาให้ จะเห็นผลเป็นดังนี้
                    อันหลังใช้ –sameseed จะเห็นว่า แม้จุดเริ่มต้นแทบจะเหมือนกัน 100% แต่ผลตอนจบก็สามารถแตกต่างกันได้อยู่ดี (ดังนั้นผลก็ขึ้นกับโชคด้วยในระดับนึง)

                    red, yellow --seed 1 --video
                    red, yellow –seed 1 –video
                    red, yellow –sameseed 1 –video

                    แปลว่าเราควรจะ Gen หลายๆ รอบ จนกว่าจะได้รูปที่ใกล้เคียงกับสิ่งที่ต้องการ แล้วพยายามหาเลข Seed ของมันให้ได้ เพื่อที่จะ Gen ให้ได้ตัวที่ใกล้เคียงภาพชุดนั้นอีกรอบ (ซึ่งเท่าที่ผมลอง มัน Work กว่าการกด V มากๆ เพราะ V ชอบออกมาเบี้ยวๆ บูดๆ)

                    วิธีหาเลข seed

                    เราสามารถรู้เลข seed ของรูปใดๆ ที่เราทำก็ได้ ด้วยการส่ง DM ไปหา MidJourney Bot ในรูปที่มันต้องการ มันจะส่งเลข Seed กลับมาให้ทาง Message ครับ

                    เช่น ผม gen ด้วย prompt แบบนี้ 2 รอบ แบบไม่กำหนด seed เลย

                    woman , wolf

                    ปรากฎว่าผมได้ 2 set นี้มา (ซึ่งคนละ seed กันแน่นอน)

                    ทีนี้ถ้าผมอยากรู้ว่าภาพชุดแรก seed อะไร ผมสามารถส่ง Emoji รูปจดหมายไปหา MJ Bot ได้ แบบนี้

                    ทีนี้ถ้าผมลอง Gen ด้วย seed ที่มันส่งให้มา ผมจะได้ผลลัพธ์ใกล้เคียงอันแรกมากๆ ลองเทียบกันได้

                    สรุปสาระ…

                    การ Gen ภาพ ด้วย MidJourney แต่ละครั้งจะไม่มีทางได้ผลลัพธ์เหมือนกัน 100% แม้จะใช้คำสั่งเดิมเป๊ะๆๆๆ มันจะมีการ Random อยู่ในระดับนึงเสมอ แม้จะใช้ seed เดียวกันก็ตามนะครับ แต่ว่าเราก็สามารถทำให้มันคล้ายเดิมมากๆ ด้วยการ Gen หลายๆ ที จนกว่าเราจะเจอ Seed ที่เราค่อนข้างชอบ แล้วสั่งให้รัน Seed นั้นอีกหลายๆ รอบได้เลย จนกว่ามันจะ Perfect

                    สรุป เรื่อง Text Prompts

                    • ตัวพิมพ์เล็กพิมพ์ใหญ่ไม่มีผลใดๆ ทั้งสิ้น (มีระบุในคู่มือ)
                    • ตัวคั่นแต่ละตัว ให้ผลไม่ต่างกันมากในการ Gen ภาพตั้งต้น เช่น space comma + – หรือ :: (อันนี้จากการทดสอบ)
                      • ยกเว้นคำว่า and (อาจรวมถึง &) อันนี้ค่อนข้างให้ผลต่างมากๆ คิดว่ามันตีความว่าไม่ใช่ตัวคั่น แต่เป็นการบังคับให้มีวัตถุหลายอัน

                    ลองดูผลการทดลองข้างล่างนี้ได้ครับ ว่าเห็นด้วยกับที่ผมสรุปหรือไม่?…

                    ผลของการใช้ตัวคั่นแบบต่างๆ (Gen อย่างละ 2 รอบ)

                    Space (เว้นวรรคเฉยๆ)

                    woman wolf --seed 1 --video

                    comma

                    woman , wolf --seed 1 --video

                    เครื่องหมาย +

                    woman + wolf --seed 1 --video

                    เครื่องหมาย :: (hard break)

                    woman :: wolf --seed 1 --video

                    เครื่องหมาย &

                    woman & wolf --seed 1 --video

                    คำว่า and

                    (อันนี้ต่างจากตัวคั่นอื่นๆ อยากชัดเจน ว่าให้แยกออกมา 2 สิ่ง)

                    woman and wolf --seed 1 --video

                    ทดลองเล่นๆ

                    ผลลัพธ์ของการ Gen ด้วยตัวคั่นต่างๆ แล้วให้ Stop ที่ 10% ตอนแรกจะแทบไม่ต่างกันเลย

                    ผลลัพธ์ของการ Gen ด้วยตัวคั่นต่างๆ แล้วให้ Stop ที่ 20% จะเริ่มเห็นความต่างแล้ว (โดยเฉพาะ & กับ and ที่จะไม่เหมือนตัวอื่น)

                    ผลลัพธ์ของการ Gen ด้วยตัวคั่นต่างๆ แล้วให้ Stop ที่ 50% อันนี้จะมีบางตัวต่างออกมาชัดเจน

                    ผลลัพธ์ของการ Gen ด้วยตัวคั่นต่างๆ แล้วให้ Stop ที่ 80%

                    ผลลัพธ์ของการ Gen ด้วยตัวคั่นต่างๆ แล้วปล่อยให้สำเร็จ 100%

                    การใช้ Image Prompt

                    เราสามารถใช้ Image Prompt มาช่วยให้เกิดผลลัพธ์ที่ตรงใจมากขึ้นได้ ซึ่งใน Community Feed หลายๆ อันก็เป็นแบบนั้น เช่น

                    รูปซ้ายสุดนี้ (จาก link นี้ ) ถ้าเราไล่ที่มาไปเรื่อยๆ เราจะพบว่ามีการใช้ Image Prompt ช่วยด้วยเช่นกัน

                    ซึ่งคำสั่งที่ทำให้ได้ 4 รูปกลางคืออันนี้ (เราจะพบว่าใน Code กำหนด Weight ของรูป Image Prompt เป็น 1 ซึ่งจะมีอธิบายอีกที)

                    <https://s.mj.run/emIdpdisrpM> a 2d illustration of a female warrior in armor, full body character concept, anime style, by hideo minaba, pixiv, artstation, granblue fantasy --iw 1 --ar 10:16 --q 2

                    ถ้าผมเอาไปรันบ้างจะได้แบบนี้ เทียบกันจะๆ แบบมี Image Prompt กับไม่มี ซึ่งจะพบว่าถ้าผมไม่ได้ใช้ Image Prompt นี่ผลลัพธ์ออกมาเป็นคนละคนกันเลย 555

                    สรุปการใช้ Advance Text Weights ด้วย ::

                    แม้ว่าเนื้อหาข้างบนผมบอกว่าตัวคั่นแต่ละตัวจะให้ผลคล้ายๆ กัน อย่างไรก็ตาม ตัวคั่น Hard Break คือ :: (Colon 2 อันติดกัน) จะมีความสามารถพิเศษ คือ เป็นตัวช่วยกำหนด Weights หรือน้ำหนักความสำคัญของ keyword ได้ ซึ่งถ้าเรียนรู้ตัวนี้ดีๆ จะช่วยให้สร้างภาพที่ตรงใจมากขึ้นได้ ดังนี้

                    ใน Text Prompts เราสามารถใส่ตัวคั่น Hard Break ได้หลายตัว โดยเราสามารถกำหนดความสำคัญให้แต่ละคำ แยกกันได้ โดย ใส่เลข weights ลง หลังเครื่องหมาย :: (แบบห้ามเว้นวรรค) เช่น

                    word1 ::weight1 word2 ::weight2 word3 ::weight3

                    แต่ถ้าไม่ใส่เลขใดๆ จะได้ Weights 1 โดยอัตโนมัติ

                    woman :: wolf :: tree

                    จะได้ผลลัพธ์แบบนี้ เพราะทุกตัว Weight 1 เท่ากันหมด มันจะดูปนๆ กันนัวๆ หน่อย (ลองรัน 2 รอบ)

                    แต่ถ้าเราระบุเลข Weight ตัวอย่างเช่น

                    woman :: wolf ::5 tree ::4

                    แปลว่า จากน้ำหนักรวม 1+5+4 =10…

                    • woman ไม่ระบุ = ได้น้ำหนัก 1 นั่นคือ 1/10 = 10%
                    • wolf ได้น้ำหนัก 5 นั่นคือ 5/10 = 50%
                    • tree ได้น้ำหนัก 4 นั่นคือ 4/10 = 40%
                    เน้นหมาป่า กับ ต้นไม้
                    woman :: wolf ::5 tree ::4
                    woman ::5 wolf ::1 tree ::4

                    แปลว่า จากน้ำหนักรวม 5+1+4 =10…

                    • woman ได้น้ำหนัก 5 นั่นคือ 5/10 = 50%
                    • wolf ได้น้ำหนัก 1 นั่นคือ 1/10 = 10%
                    • tree ได้น้ำหนัก 4 นั่นคือ 4/10 = 40%
                    เน้นผู้หญิง กับ ต้นไม้
                    woman ::5 wolf ::1 tree ::4

                    ซึ่งน้ำหนักสามารถใส่เลขทศนิยมได้ และติดลบได้ด้วย (ติดลบ คือ ในภาพไม่ควรมีสิ่งนั้น ) เช่น moon ::-0.5 มีผลเท่ากับ –no moon

                    woman :: wolf :: tree :: moon ::-0.5 --seed 1 --video
                    ห้ามมีพระจันทร์
                    woman :: wolf :: tree :: moon ::-0.5 ground::-0.5 --seed 1 --video
                    ห้ามมีพระจันทร์ ห้ามมีพื้น

                    อย่างไรก็ตาม Weights รวมทั้งหมดของ Prompts ทุกอัน ห้ามติดลบนะครับ เช่นใส่ว่า xxx ::0.2 yyy ::-0.5 แบบนี้ไม่ได้นะ เพราะรวมกันได้ -0.3

                    ถ้ามี Image Prompt ด้วย

                    และถ้ามี Image Prompt ด้วย ก็จะมีผลกับ Weight เช่นกัน ซึ่งกำหนดได้โดยค่า –iw เช่น

                    เราสามารถกดปุ่ม + เพื่อ Upload Image เข้า Discord เพื่อเอามาเป็น Image Prompt ได้นะ จากนั้นให้คลิ๊กขวาแล้ว Copy Link

                    ทำความเข้าใจวิธีสั่ง MidJourney แบบละเอียด เพื่อให้ได้ผลลัพธ์ตรงใจ (มากขึ้น) 556
                    Step1 : กดปุ่ม + เพื่อ Upload Image
                    ทำความเข้าใจวิธีสั่ง MidJourney แบบละเอียด เพื่อให้ได้ผลลัพธ์ตรงใจ (มากขึ้น) 557
                    Step2 : คลิ๊กขวาแล้ว Copy Link
                    ทำความเข้าใจวิธีสั่ง MidJourney แบบละเอียด เพื่อให้ได้ผลลัพธ์ตรงใจ (มากขึ้น) 558
                    รูปที่ใช้เป็น Image Prompt https://cdn.discordapp.com/attachments/1004932439333928981/1009096482529489016/seal-4923333_1920.jpg

                    ผลที่ได้คือแบบนี้

                    https://cdn.discordapp.com/attachments/1004932439333928981/1009096482529489016/seal-4923333_1920.jpg woman :: wolf ::5 tree ::4 --iw 10 --seed 1 --video

                    อันนี้เน้นรูปแมวน้ำ และ หมาป่า และต้นไม้

                    แปลว่า จากน้ำหนักรวม 1+5+4+10 =20…

                    • woman ได้น้ำหนัก 1 นั่นคือ 1/20 = 5%
                    • wolf ได้น้ำหนัก 1 นั่นคือ 5/20 = 25%
                    • tree ได้น้ำหนัก 4 นั่นคือ 4/20 = 20%
                    • image prompt = ได้น้ำหนัก –iw 10 นั่นคือ 10/20 = 50%
                    https://cdn.discordapp.com/attachments/1004932439333928981/1009096482529489016/seal-4923333_1920.jpg woman ::5 wolf ::1 tree ::4 --iw 5 --seed 1 --video

                    อันนี้เน้นรูปแมวน้ำ และ ผู้หญิง และต้นไม้

                    แปลว่า จากน้ำหนักรวม 5+1+4+5 =15…

                    • woman ได้น้ำหนัก 5 นั่นคือ 5/15 = 33.33%
                    • wolf ได้น้ำหนัก 1 นั่นคือ 1/15 = 6.67%
                    • tree ได้น้ำหนัก 4 นั่นคือ 4/15 = 26.67%
                    • image prompt = ได้น้ำหนัก –iw 5 นั่นคือ 5/15 = 33.33%

                    Tips: วิธี Copy Link รูปกรณีที่ใช้มือถือ

                    ให้จิ้มที่รูปค้างไว้ แล้วเลือก Copy Media Link ได้เลยครับ

                    ทำความเข้าใจวิธีสั่ง MidJourney แบบละเอียด เพื่อให้ได้ผลลัพธ์ตรงใจ (มากขึ้น) 559

                    สัดส่วนรูปมีผลยังไง?

                    สัดส่วน Aspect Ratio ของรูป นั้นส่งผลต่อผลลัพธ์สุดท้ายอย่างชัดเจน (แม้ตอนแรกจะดูคล้ายกัน) เรามารถดูได้จากคลิปนี้

                    woman :: wolf –seed 1 –video –ar 1:1
                    woman :: wolf –seed 1 –video –ar 9:16

                    ดังนั้นเราต้องคิดให้ดีว่าผลลัพธ์สุดท้ายเราต้องการรูปแนวไหนมากกว่ากัน เช่น แนวนอนจะเหมาะกับภาพที่มีวิวเยอะๆ แนวตั้งเหมาะกับภาพคนหรือตัวละครมากกว่า เป็นต้น

                    จะสมัครแบบจ่ายตังทำไง?

                    อ่านมาถึงตรงนี้หลายคนคงเริ่มรู้ตัวแล้วว่า ยังไงการใช้แบบฟรีมันไม่มีทาง Gen รูปได้ถึงระดับที่เราพอใจได้แน่ๆ เพราะมันมีปัจจัยเรื่องโชคและการ Random อยู่พอสมควร ดังนั้นถ้าใครสนใจสมัครแบบจ่ายเงิน กดสามารถพิมพ์คำสั่งนี้ได้

                    /subscribe

                    มันจะมี Link พาไปเว็บที่มีรายละเอียดการจ่ายเงินเลยครับ ซึ่งหลักๆ ถ้าเป็นของคนปกติจะมี 2 แบบ

                    • Basic : 10$ ต่อเดือน
                      • ได้เวลา gpu-minutes 200 นาที ต่อเดือน
                      • นั่นคือ gen ได้ “ประมาณ” 200 ครั้ง (โดยเฉลี่ยมัน Gen รูปนึงใช้เวลา 1 นาที) เพราะโควต้ามันไม่ได้นับเป็นรูป แต่นับเป็นเวลา
                    • Standard : 30$ ต่อเดือน
                      • ได้เวลา gpu-minutes 15 ชั่วโมง ต่อเดือน (โหมด Fast) แต่ถ้าสลับเป็นโหมด Relax จะใช้ได้ Unlimited ครับ
                      • อย่างไรก็ตาม Max Upscale ต้องใช้ Fast Mode เท่านั้น
                      • ถ้าใช้ Fast Mode จนหมดโควต้า สามารถจ่ายซื้อเพิ่มเป็นรายชั่วโมงได้

                    เราสามารถดูสถานะปัจจุบันได้ด้วยคำสั่งนี้

                    /info

                    เช่นอันนี้ของผม ณ ตอนนี้ (ใช้มาตั้งแต่ 5 สค. – 17 สค. 2022 นั่นคือประมาณ 11 วัน Gen ไปพันกว่ารูป)

                    ทำความเข้าใจวิธีสั่ง MidJourney แบบละเอียด เพื่อให้ได้ผลลัพธ์ตรงใจ (มากขึ้น) 560

                    จะเห็นว่ามีโควต้า Fast ทั้งหมด 15 ชม. จริงๆ และตอนนี้เหลือ 7 ชม. กว่าๆ เอง (เพราะตอนแรกไม่รู้ว่ามีโควต้านี้ และควรปรับเป็นโหมด /relax ถ้าไม่รีบ 555)

                    ดังนั้นถ้าไม่ติดขัดเรื่องเงินมากนัก แบบ 30$ คุ้มกว่ามากๆๆๆ ครับ ถ้าใช้แบบ 10$ นี่คือหมดไปเป็นชาติแล้ว

                    ก่อนจากลา

                    สุดท้ายนี้ สำหรับบทความนี้ผมขอจบเท่านี้ก่อน แม้ว่าผมก็ยังต้องทดลองต่อไปอีกหลายอย่างจึงจะเข้าใจมันลึกซึ้งมากขึ้น แต่อยากรีบเอามาแชร์ก่อนเผื่อจะเป็นประโยชน์กับเพื่อนๆ ครับ ถ้าผมรู้อะไรดีๆ เพิ่มจะเอามาพิมพ์เพิ่มให้เรื่อยๆ นะ

                    ถ้าใครรู้เทคนิคอะไรดีๆ ก็ Comment บอกได้เลยนะ

                  • สรุปเรื่องของ DAX ฉบับเทพเอ็กเซล

                    สรุปเรื่องของ DAX ฉบับเทพเอ็กเซล

                    Post นี้เป็นการนั่งสรุปความคิดของผมเกี่ยวกับ DAX ซึ่งเป็นภาษาที่ใช้ใน Data Model ของทั้ง Excel และ Power BI ซึ่งแม้จะเป็นภาษาที่หน้าตาเหมือนกับสูตร Excel แต่หลักการทำงานหลายๆ อย่างเป็นเรื่องที่แนวคิดไม่เหมือนกับสูตร Excel เลย ดังนั้นแม้จะเก่งสูตร Excel มาจากไหนก็ตาม ก็ยังต้องมานั่งเรียนรู้ DAX ใหม่อยู่ดี (แต่ก็คุ้ม เพราะภาษา DAX ความสามารถมันเจ๋งมากๆๆๆๆ)

                    ลักษณะของบทความนี้จะเป็นการที่ผมนั่งทด นั่งคุยกับตัวเองถึงประเด็นสำคัญๆ และประเด็นที่ต้องระวัง ดังนั้นมันอาจจะกระโดดไปมาบ้าง ยากบ้าง ง่ายบ้าง แต่หวังว่ามันจะมีประโยชน์กับเพื่อนๆ ที่แวะมาดูนะครับ อีกอย่างผมจะพยายามมาปรับปรุงบทความนี้อยู่เรื่อยๆ ตามความรู้ที่ผมมี ดังนั้นแวะมาอ่านบ่อยๆ ได้เลยครับ

                    ผลลัพธ์ของสูตร DAX

                    ผลลัพธ์ของ DAX มีทั้งออกมาเป็นค่าเดียว (Scalar) และออกมาเป็นตาราง (Table)

                    • ถ้าเขียน New Measure ผลลัพธ์สุดท้ายต้องเป็น Scalar
                    • ถ้า Add New Column ผลลัพธ์ในแต่ละบรรทัดเป็น Scalar
                    • ถ้า Add New Table ผลลัพธ์ต้องเป็น Table
                    • อย่างไรก็ตาม รายละเอียดในส่วนประกอบปลีกย่อยจะเป็นอะไรก็ได้ และประกาศตัวแปรด้วย VAR ได้ทุกประเภท

                    หมายเหตุ : Table ที่มีแค่ 1 Column และ 1 Row ตัว DAX จะสามารถแปลงเป็น Scalar ให้โดยอัตโนมัติ (มองได้ 2 แบบ)

                    tips : การสร้าง New Col ใน DAX ได้ผลเร็วกว่าสร้างใน Power Query แต่จะทำให้ขนาด Model ใหญ่ขึ้น (เพราะไม่สามารถ optimize ได้เต็มที่)

                    การอ้างอิงสูตร DAX

                    • อ้างอิงตาราง = TableName
                    • อ้างอิงคอลัมน์ =TableName[ColumnName]
                    • อ้างอิง Measure = [MeasureName] (ไม่ต้องมีชื่อตาราง จะได้ไม่สับสนกับคอลัมน์)

                    Tips : แนะนำว่าถ้าสร้างคอลัมน์จำลองในสูตร ควรตั้งชื่อคอลัมน์ด้วย “@ColumnName” เพื่อให้อ้างอิงด้วย [@ColumnName] จะได้ไม่สับสนกับ Measure

                    Evaluation Context คือ หัวใจของ DAX

                    การใช้ DAX นั้นต่างจากสูตร Excel ตรงที่เราต้องพิจารณาถึง Evaluation Context อยู่ตลอดเวลา ซึ่งการใช้สูตรแต่ละที่ก็จะมีบริษทที่แตกต่างกัน ซึ่งบริบทที่ว่ามีอยู่ทั้งหมด 2 แบบคือ Filter Context และ Row Context

                    Filter Context = ณ จุดนั้นมีการ Filter อะไรอยู่บ้าง?

                    • ในตาราง Data : ปกติจะไม่มีการ Filter ดังนั้นถ้า Add Column แล้วเขียนว่า =SUM(TableName[ColumnName]) จะได้ค่าเท่ากันหมดทุกบรรทัด (เพราะไม่มีการ Filter นั่นเอง)
                    สรุปเรื่องของ DAX ฉบับเทพเอ็กเซล 561
                    SUM Revenue ออกมาเท่ากันหมดทุกบรรทัด
                    • ใน Report : อาจจะมีการ Filter ตาม Category/Axis หรือ Filter ที่ใส่ลงไป หรือแม้กระทั่ง Filter จาก Interaction ในรายงาน และนั่นคือสาเหตุที่เขียนสูตร Measure ว่า =SUM(TableName[ColumnName]) เหมือนกัน แต่ในแต่ละ Category จะได้ค่าไม่เท่ากัน (เพราะ Filter ไม่เหมือนกัน)

                    Row Context = การวน Loop เพื่อพิจารณาทีละแถวของตาราง

                    • ในตาราง Data : การเขียนสูตรเพื่อวน Loop ในแต่ละบรรทัดของตารางจะมี Row Context เพื่อพิจารณาทีละแถว เช่น New Column, หรือสูตรพวก Iterator อย่าง SUMX, FILTER, ADDCOLUMNS ดังนั้นการเขียนสูตรว่า =TableName[ColumnName] จึงมองเห็นแค่บรรทัดนั้นๆ ของคอลัมน์ที่ระบุเท่านั้น
                    • ใน Report : ปกติจะไม่มี Row Context ดังนั้นจะเขียน Measure ลง Report ว่า =TableName[ColumnName] ไม่ได้ เพราะมันไม่ได้ดูทีละแถว

                    การจะตรวจสอบ Level ของ Measure

                    เนื่องจากในรายงาน ไม่มี Row Context ดังนั้น หากเราต้องการตรวจสอบว่า Measure กำลังทำงานอยู่ Category ที่ไหน ระดับไหน

                    สรุปเรื่องของ DAX ฉบับเทพเอ็กเซล 562
                    • ISINSCOPE / ISFILTERED เพื่อเช็คว่าอยู่ Level ไหน แต่ ISINSCOPE จะตรวจสอบ Level ได้ชัดเจนกว่า (ISFILTERD จะได้รับผลมาจาก Visual อื่นได้ ทำให้สับสน)
                    • SELECTEDVALUE เพื่อตรวจสอบว่ากำลังอยู่แกนชื่อว่าอะไร (ถ้า SELECTEDVALUE มองเห็นคอลัมน์ที่ระบุแค่ค่าเดียว จะได้ค่านั้นกลับมา)

                    FILTER

                    • เป็น Table Function ที่คัดกรองผลลัพธ์ให้เหลือแถวน้อยลง โดยมองเห็นคอลัมน์ในตารางต้นฉบับ (Row Context ของตารางต้นฉบับ)
                    • เงื่อยไขใส่ได้ตัวเดียว แต่ใช้ AND, OR ช่วยได้ (แต่ได้คู่เดียว)
                    • ใช้ && แทน AND จะยืดหยุ่นกว่า
                    • ใช้ II แทน OR จะยืดหยุ่นกว่า
                    Product[Color] IN {"Red","Blue","Yellow"}

                    มีค่าเท่ากับ

                    CONTAINSROW ( {"Red","Blue","Yellow"} , Product[Color] )

                    ALL vs VALUES vs DISTINCT

                    กรณีอ้างอิงไปที่คอลัมน์เดียว

                    • ALL ได้ผลลัพธ์แบบไม่ซ้ำ แบบมี Blank Row พิเศษได้ และ ปลด Filter ด้วย
                    • VALUES ได้ผลลัพธ์แบบไม่ซ้ำ แบบมี Blank Row พิเศษได้ (ไม่ปลด Filter)
                    • DISTINCT ได้ผลลัพธ์แบบไม่ซ้ำ (ไม่มี Blank Row พิเศษ ไม่ปลด Filter)

                    Blank Row พิเศษเกิดขึ้นในฝั่งตารางที่เป็นเลข 1 เวลาจับคู่กับอีกตารางผ่าน Relationship ไม่ได้ (ในอีกตารางฝั่ง * มีค่าบางอย่างที่ตารางฝั่งเลข 1 ไม่มี) เลยสร้าง Blank Row มาจับคู่แทน (แต่จะสร้าง Blank Row ขึ้นมาตัวเดียว แม้จะจับไม่ได้หลายคู่)

                    CALCULATE / CALCULATETABLE

                    • เป็นฟังก์ชันพวกเดียวที่สามารถเปลี่ยนแปลง Filter Context ได้
                    • Filter Argument ของ CALCULATE จริงๆ แล้วถูกมองเป็น Table เสมอ
                      เช่น การเขียนเป็นเงื่อนไข (เรียกว่า predicate) ว่า
                    Product[Brand]="Contoso" 

                    มีค่าเทียบเท่ากับ

                    FILTER( ALL( Product[Brand] ), Product[Brand]="Contoso" )
                    • คอลัมน์ที่จะอ้างถึง ต้องมีจริงใน Data Model เท่านั้น (ถ้าไม่มีจริง ต้องเขียนด้วยการใช้ FILTER มาช่วย)
                    • แต่ถ้าใช้ KEEPFILTERS ครอบจะไม่มีการปลด Filter เดิมออกเลย
                    • การใช้ ALL, REMOVEFILTERS ใน argument ของ CALCULATE แบบตรงๆ จะทำหน้าที่เป็น Calculate Modifier ซึ่งทำหน้าที่ปลด Filter (ไม่ได้ทำหน้าที่เป็น Table Function ที่สร้างตารางผลลัพธ์ที่ไม่ซ้ำเหมือนปกติ)
                    • เวลาเขียน CALCULATE ซ้อนกัน วิธีคิดจะแปลก เพราะ เนื่องจากการใช้ฟังก์ชันซ้อนกัน ปกติจะคิดตัวในก่อน ซึ่งพอไปมองไปที่ CALCULATE ตัวใน CALCULATE จะต้องตีความ Filter Context ที่มีผลต่อมันก่อน (ซึ่งมาจาก CALCULATE ตัวนอก) ดังนั้น ผล Filter ของ CALCULATE ตัวนอกจึงถูกคิดก่อน แล้วค่อยมาคิด Condition ตัวในทีหลัง (ซึ่งมันอาจจะทับตัวนอกได้)

                    Context Transition

                    • CALCULATE/CALCULATETABLE สามารถเปลี่ยน Row Context ให้กลายเป็น Filter Context ได้ (แต่ต้องระวังบรรทัดที่ข้อมูลเบิ้ล ผลลัพธ์จะผิด)
                    • เวลาอ้างอิง Measure จะมี CALCULATE ครอบอยู่เสมอ ทำให้เกิด Context Transition เสมอ
                    สรุปเรื่องของ DAX ฉบับเทพเอ็กเซล 563
                    Product Revenue = [Total Revenue] มี Context Transition (เหมือนมี CALCULATE มาครอบ อัตโนมัติ)
                    แม้ว่า Total Revenue = SUM(OrderDetail[Revenue])
                    • Context Transition เกิดขึ้นก่อน Calculate Modifier แปลว่า เราสามารถใช้ ALL ปลดผลจาก Context Transition ได้ (ถ้าเราใส่ ALL เพิ่มเข้าไป Engine จะฉลาดและรู้ว่าไม่ต้องทำ Context Transition แล้ว)
                    • เวลาเกิด Context Transition หลายคอลัมน์ ระวังเรื่อง Circular Dependency เพราะมันจะอ้างอิงกันเอง ซึ่งแก้ไขได้โดยให้ในตารางมีคอลัมน์ที่มี Unique Value อยู่และทำให้ Engine รู้ นั่นคือ ทำให้อยู่ฝั่งเลข 1 ของ Relationship ซะ หรือ Mark as Date Table ก็ได้

                    CALCULATE Modifier

                    • USERELATIONSHIP เลือกเส้น Relationship ที่จะ Active
                    • CROSSFILTER เลือกวิธีการไหลของ Relationship
                    • ALL ทั้งหลาย และ REMOVEFILTERS ใช้ปลด Filter

                    ลำดับการคำนวณของ CALCULATE

                    1. พิจารณาเงื่อนไขใน filter argument ของ calculate เอง (และ row context) ภายใต้ context original แล้วจำไว้ก่อน
                    2. ทำการแปลง row context (ถ้ามี) เป็น filter context แล้วเอาไปรวมกับ filter argument ที่จำไว้
                    3. พิจารณา filter context ดั้งเดิมทั้งหมด
                    4. ทำ Calculate modifier เช่น ALL, REMOVEFILTERS, USERELATIONSHIP, CROSSFILTER ซึ่งจะดัดแปลง context เดิมได้
                    5. ทำตาม filter argument ที่จำเอาไว้ทั้งหมด ซึ่งอาจเปลี่ยนแปลง context เดิมได้ สุดท้ายกลายเป็น filter context ใหม่
                    6. คำนวณ expression ออกมาภายใต้ filter context ใหม่

                    เทคนิคพิเศษกับ CALCULATE

                    • เราสามารถปลด Filter ออกด้วย ALL() ก่อน แล้วใส่ Filter กับเข้าไปใหม่ด้วย VALUES หรือจะใช้ ALLEXCEPT ก็ได้
                    • ALLEXCEPT คือการปลดออก ไม่ได้มีการใส่อะไรเข้าไปแทน
                    • ALL + VALUES คือ ปลดออก + ใส่กลับ

                    การอ้างอิงคอลัมน์ Date ใน Date Table เป็นการปลด Filter ทั้งตาราง Date (ปกติต้องอ้างคอลัมน์นั้นตรงๆ ถึงจะปลดได้ แต่กับคอลัมน์ Date จะเป็นกรณีพิเศษ ซึ่งหลักการนี้ใช้กับ Time Intelligent ด้วย)

                    สรุปเรื่องของ DAX ฉบับเทพเอ็กเซล 564

                    VAR

                    • VAR ถูกคำนวณค่าครั้งเดียวใน Scope ของ VAR นั้นๆ แล้วเก็บผลลัพธ์ไว้เป็น Constant (แปลว่าเปลี่ยนด้วย CALCULATE ไม่ได้)
                    • VAR หลายทีได้ อ้างอิงตัวก่อนหน้าได้
                    • VAR ซ้อนกันหลายชั้นได้ เช่น
                    test = 
                    VAR aaa = 1
                    VAR bbb = 2 * ( 
                                      VAR xxx = 3
                                      VAR yyy = 4
                    
                                      RETURN xxx+yyy-aaa  // ในนี้มองเห็น aaa
                                    )
                    VAR ccc = 5
                    RETURN aaa/bbb+ccc  // ในนี้มองไม่เห็น xxx
                    • เรามักใช้ VAR เก็บค่าที่ต้องการเอาไว้ ซึ่งจะเก็บ Scalar หรือ Table ก็ได้
                    • VAR ช่วยให้อ่านสูตรง่ายขึ้น แบ่งสูตรเป็นขั้นเป็นตอน

                    RANKX

                    RANKX ( <Table>, <Expression> ,[<Value>] ,[<Order>] ,[<Ties>] )
                    1. RANKX จะสร้าง LookupTable จาก <Table> แล้ว ประเมินค่า <Expression> ใน Row Context ของตาราง LookupTable
                    2. จะประเมินค่า <Value> ใน Filter Context ปัจจุบัน ไปเทียบอันดับใน Lookup Table
                    3. ถ้าไม่ได้ระบุ <Value> จะเอา <Expression> ใน Filter Context ปัจจุบัน ไปเทียบอันดับใน Lookup Table แทน

                    Tips : เวลาใช้ใน Measure อย่าลืมปลด Filter ของ <Table> ด้วย เดี๋ยวจะมองไม่เห็นค่าใน Category อื่น

                    Time Intelligence

                    • อย่าลืมสร้าง Date Table และ Mark as Date Table ด้วย เพื่อให้การปลด Filter ที่คอลัมน์ Date จะเทียบเท่ากับการปลดทั้ง Date Table เสมอ Time Intelligent จึงจะทำงานถูกต้อง
                    • Time Intelligence ด้วยหลักการ คือ การเปลี่ยนช่วงเวลาของ Filter Context (เอาไว้ใช้ใน CALCULATE)
                    • การอ้างถึงคอลัมน์ Date ใน DateTable ของฟังก์ชัน Time Intelligent จริงๆ แล้วคือการย่อของการอ้างอิงถึงตาราง ดังนี้
                    DATESYTD( dDate[Date] )

                    มีค่าเท่ากับ

                    DATESYTD( CALCULATETABLE( DISTINCT( dDate[Date] ) ) )
                    • สังเกตว่ามี CALCULATETABLE แปลว่าเกิด Context Transition ได้
                    • และการที่มันรับข้อมูลเป็นตาราง แปลว่าเราซ้อน Time Intelligent หลายตัวผสมกันได้ (เพราะผลของ Time Intelligent คือตารางที่มีคอลัมน์วันที่)

                    ฟังก์ชัน Time Intelligence

                    • DATESYTD = แก้ช่วงเวลาให้เริ่มตั้งแต่ วันแรกของปีเดียวกับปีสุดท้ายของ Filter Context ถึง วันสุดท้ายของ Filter Context
                      (วันแรกของปี คือวันถัดจากวันสุดท้ายของปีงบประมาณ ซึ่งจะมีปัญหาถ้าสิ้นปีงบประมาณที่สิ้นกุมภา)
                    • DATEADD =
                      • เลื่อนเวลาไปโดยกำหนดช่วงเวลาและหน่วยได้อิสระ (ยืดหยุ่นกว่า SAMEPERIODLASTYEAR)
                      • ผลลัพธ์คือช่วงวันที่ที่มีอยู่ในตารางวันที่ (มันมองไม่เห็นวันที่อยู่นอกช่วง)
                      • ถ้าเลื่อนไปแล้ววันเกิน จะเอาวันสิ้นเดือนมาแทน
                      • ถ้าต้นฉบับมี 2 วันสุดท้ายของเดือน วันที่เลื่อนไปจะได้วันเหมือนต้นฉบับจนถึงวันสิ้นเดือนด้วย
                    • DATESBETWEEN = ได้ช่วงวันที่ระหว่างวันที่กำหนด
                    • DATESINPERIOD = ได้ช่วงวันที่ตั้งแต่วันที่หนด ด้วยระยะเวลาที่กำหนด
                    • PARALLELPERIOD = ได้ช่วงที่ครบ Period ที่ระบุตั้งแต่ต้นจบจบ
                    • FIRSTDATE / LASTDATE = เหมือน MIN/MAX แต่ว่าได้ผลเป็นตารางที่มี 1 วัน และมี Context Transition ด้วย

                    ต้องระวัง Time Intelligent ว่า ปกติมันจะ ปลด Filter ทั้ง Date Table ตอนที่เปลี่ยนช่วงเวลา ทำให้บางครั้งผลลัพธ์ก็อาจจะผิด เช่น มีการ Filter วันประจำสัปดาห์ด้วย แบบนี้ก็จะผิด เพราะของปีก่อนหน้าก็ไม่ใช่วันจันทร์แล้ว และ YTD ก็ไม่ได้มีแต่วันจันทร์ (แค่ต้นปีจนถึงจันทร์สุดท้ายเฉยๆ)

                    สรุปเรื่องของ DAX ฉบับเทพเอ็กเซล 565

                    Date Lineage และ TREATAS

                    • เมื่อค่าใดๆ อ้างถึง Column ที่มีอยู่จริง มันจะได้ Data Lineage ของคอลัมน์นั้นมาด้วย ดังนั้นการ Filter จะมีความหมายตามคอลัมน์นั้นจริงๆ
                    • แต่ถ้าเรามีการสร้างคอลัมน์ใหม่ขึ้นมา หรือเอาคอลัมน์เดิมมาคำนวณเพิ่ม Data Lineage จะหายไป มันจะมองเป็นคอลัมน์ที่ไม่ได้มีความหมายในเชิง Model อะไร
                    • ฟังก์ชันที่จะเปลี่ยน Data Lineage ได้ก็คือ TREATAS (เอาไปใช้ใน CALCULATE ได้ หรือจะเก็บใน VAR ก่อนใช้ก็ได้)
                    TREATAS ( <Expression>, <ColumnName> , [<ColumnName>],... )

                    TREATAS จะตามด้วย <Expression> ซึ่งคือ ตารางที่มีคอลัมน์ที่อาจยังไม่มี Data Lineage แล้วตามด้วยคอลัมน์ที่อยากจะไปดึง Data Lineage มาใช้ ซึ่งต้องระบุให้ครบทุกคอลัมน์ของตารางใน Expression ด้วย เช่น

                    TREATAS ( 
                       {
                           (2007,"December"),
                           (2008,"February")
                       }
                       , 
                       dDate[Year],
                       dDate[MonthName],
                       )  
                    // แปลว่าเอาให้ตารางที่มีค่าคำว่า Red กับ Blue ได้ Data Lineage เทียบเท่ากับเป็นคอลัมน์ Product[Color]

                    Advanced Table Function

                    • ADDCOLUMNS ใช้เพิ่มคอลัมน์ใหม่เข้าไปในตาราง
                    • SUMMARIZE สร้างตารางขึ้นมา โดยทำการ Scan ตารางที่ระบุ แล้ว Group Related Column ที่ระบุอีกที
                      • ดีกว่าพวก ALL ตรงที่ SUMMARIZE จะเอาจากตารางเดียวกันหรือตารางอื่นก็ได้ เจ๋งมากๆ
                      • แม้จะสร้างคอลัมน์คำนวณขึ้นมาใหม่ได้ แต่ไม่ควรทำ เพราะสร้างทั้ง Row Context และ Filter Context ขึ้นพร้อมกัน ให้ใช้ SUMMARIZE + ADDCOLUMNS ดีกว่า
                    • CROSSJOIN ใช้สร้าง Combination ทุกอันที่เป็นไปได้ของค่าใน Input Table
                      • อาจใช้ CROSSJOIN + FILTER แทนการ SUMMARIZE Fact ที่จำนวนบรรทัดมากๆ
                    • UNION ใช้ Append ตารางเข้าด้วยกัน
                      • ไม่ได้ตัดตัวซ้ำ ถ้าจะตัดก็ใช้ DISTINCT ครอบอีกที
                      • จะได้ Data Lineage ก็ต่อเมื่อ ตารางต้นฉบับมี Data Lineage เดียวกันทั้งคู่
                      • ถ้าไม่ได้ Data Lineage มา อาจใช้ TREATAS หรือ CALCULATE มาช่วยจัดการภายหลัง
                    • INTERSECT ได้ตัวซ้ำระหว่าง 2 ตาราง
                      • ได้ Data Lineage ของตารางแรกมาเสมอ
                    • EXCEPT เอาตารางแรกตั้ง ลบออกด้วยตารางที่สอง
                      • ได้ Data Lineage ของตารางแรกมาเสมอ
                    • SELECTCOLUMNS ใช้เลือกคอลัมน์ที่ต้องการ เปลี่ยนชื่อคอลัมน์ได้ (ไม่ได้ลดจำนวนแถวเหมือน SUMMARIZE)
                      • ได้ Data Lineage ถ้าอ้างอิงคอลัมน์ตรงๆ ไม่ได้เป็น Expression
                    • TOPN ได้ N แถวแรกของตาราง เรียงตามที่กำหนด
                      • ถ้ามีค่าเท่ากันหลายบรรทัด อาจได้แถวมากกว่า N ที่กำหนดได้
                        (แก้โดยเพิ่มคอลัมน์เข้าไปใน Sort ของ TOPN จนได้บรรทัดที่ไม่ซ้ำกัน)
                    • GENERATE ทำการ Iterate เข้าไปในแต่ละแถวของตารางที่ระบุ (row context) แล้วเชื่อมกับผลลัพธ์ของ table expression
                    • GENERATEALL เหมือน Generate แต่ถ้า table expression เป็นค่าว่าง จะไม่ skip แต่จะปล่อยว่างไป

                    Expanded Table

                    • การอ้างอิง Table หมายถึง Expanded Table เสมอ
                    • ตาราง Expand ออกไปทางตารางที่เป็นเลข 1 (ไม่เกี่ยวกับ Filter Direction)
                    • เมื่อการ Filter ถูกทำไปที่คอลัมน์ที่กำหนด ทุก Expanded Table ที่มีคอลัมน์นั้นจะถูก Filter ทันที
                    • RELATED จริงๆ คือคำส่งที่ใช้เข้าถึงคอลัมน์ใน Expanded Table
                    • การ Filter ด้วยตาราง ต้องระวังว่ามันหมายถึง Expanded Table
                      • การ Filter ด้วยเงื่อนไขคอลัมน์ กับ การระบุด้วยตาราง เป็นคนละเรื่องโดยสิ้นเชิง
                      • ทำให้เวลาใช้ใน CALCULATE มันอาจจะมีผลกับ Filter บางอย่างที่เราอยากจะเก็บเอาไว้ก็ได้ เช่น
                      • ปลด Filter ที่ Fact ก็คือการปลด Filter ทั้ง Model ที่ RELATED กับ Fact ได้เลย
                      • หรือใส่ Filter ตาราง Fact จะได้ผลลัพธ์เฉพาะที่ RELATED กับ FACT เท่านั้น ดังนั้นลูกค้าที่ไม่ซื้อของจะหายไปเลย
                    สรุปเรื่องของ DAX ฉบับเทพเอ็กเซล 566
                    • Context Transition ก็ทำงานกับทุกคอลัมน์ใน Expanded Table ด้วยเช่นกัน

                    Relationship

                    • ถ้าจะสร้าง Calculated Column เพื่อเชื่อม Relationship ให้ระวัง Circular Dependency
                      • เลี่ยงปัญหาได้ด้วยการใช้ DISTINCT แทน VALUES, ALLNOBLANKROW แทน ALL
                      • เลี่ยงการใช้ CALCULATE แบบย่อ (เพราะมี ALL แฝง), SELECTEDVALUE (มี VALUES แฝง)
                  • วิธีแก้ปัญหาเวลาเขียน M Code แบบ each ซ้อนกันหลายอัน

                    วิธีแก้ปัญหาเวลาเขียน M Code แบบ each ซ้อนกันหลายอัน

                    มีแฟนเพจที่ติดตามอยู่ได้ถามคำถามมาว่า เวลาเขียน M Code แล้วมีการอ้างอิงฟังก์ชันซ้อนในอีกฟังก์ชันนึงจะมีปัญหาพอสมควร เช่น กรณีที่ต้องการจะ SUM ข้อมูลยอดขายของสินค้าที่ต้องการ ถ้าเราอ้างอิงชื่อสินค้าไปเลยตรงๆ จะทำได้ไม่มีปัญหา แต่ถ้าหากพยายามจะอ้างอิงสินค้าที่อยู่ในบรรทัดนั้นๆ จะพบปัญหา

                    เช่น กรณีอ้างอิงชื่อสินค้าไปเลย สามารถทำได้ โดย Add Custom Column แบบนี้ โดยใช้ Table.SelectRows เพื่อ Filter ข้อมูล MyTable ให้เหลือเฉพาะบรรทัดที่ต้องการ

                    Table.SelectRows(MyTable,each [สินค้า]="อาหาร")

                    ซึ่งจะได้ผลเป็น M Code เต็มๆ แบบนี้ ซึ่งจะมี each 2 ตัว ซ้อนกันอยู่ ซึ่งนี่แหละจะทำให้เกิดปัญหา เวลาไปอ้างอิงคอลัมน์แบบปกติ

                    = Table.AddColumn(MyTable, "ยอดขายของสินค้า", each Table.SelectRows(MyTable,each [สินค้า]="อาหาร"))
                    วิธีแก้ปัญหาเวลาเขียน M Code แบบ each ซ้อนกันหลายอัน 567

                    แต่ถ้าเราพยายามจะแก้คำว่า “อาหาร” ในสูตร ให้เป็น [สินค้า] เพื่อพยายามจะอ้างอิงสินค้าในบรรทัดนั้นๆ จะเจอปัญหาทันที เพราะสูตร [สินค้า]=[สินค้า] มันจะเป็นจริงเสมอนั่นเอง ผลลัพธ์จึงไม่มีการ Filter อะไรเลย

                    Table.SelectRows(MyTable,each [สินค้า]=[สินค้า])
                    วิธีแก้ปัญหาเวลาเขียน M Code แบบ each ซ้อนกันหลายอัน 568

                    แล้วเราจะทำให้มันไปอ้างอิงว่า ให้ Filter คอลัมน์ สินค้า ของ MyTable ให้มีค่าเท่ากับสินค้าในบรรทัดนั้นๆ ได้ยังไง?

                    เราต้องข้าใจก่อนว่า each [สินค้า] เนี่ย มันย่อมาจาก (_) => _[สินค้า] หรือ (x) =>x[สินค้า] โดย x คือ parameter ของฟังก์ชัน ซึ่งจะตั้งชื่ออะไรก็ได้ เช่น ผมอาจตั้งชื่อว่า main ก็ได้ เพื่ออ้างอิงถึง each ตัวข้างนอกสุด สรุปว่าได้แบบนี้

                    ซึ่ง main[สินค้า] จะหมายถึงคอลัมน์ของ Table.AddColumn แต่ [สินค้า] จะหมายถึงคอลัมน์ตอนที่ใช้ Table.SelectRows

                    = Table.AddColumn(MyTable, "ยอดขายของสินค้า", (main)=> Table.SelectRows(MyTable,each [สินค้า]=main[สินค้า]))
                    วิธีแก้ปัญหาเวลาเขียน M Code แบบ each ซ้อนกันหลายอัน 569

                    พออ้างอิงตารางได้แล้ว ก็เลือกเอาคอลัมน์ยอดขายออกมา ด้วยการใส่ [ยอดขาย] ต่อท้าย เช่น

                    = Table.AddColumn(MyTable, "ยอดขายของสินค้า", (main)=> Table.SelectRows(MyTable,each [สินค้า]=main[สินค้า])[ยอดขาย])
                    วิธีแก้ปัญหาเวลาเขียน M Code แบบ each ซ้อนกันหลายอัน 570

                    สุดท้ายก็ใช้ List.Sum มาทำการหาผลรวมซะ จะได้แบบนี้

                    = Table.AddColumn(MyTable, "ยอดขายของสินค้า", (main)=> List.Sum(Table.SelectRows(MyTable,each [สินค้า]=main[สินค้า])[ยอดขาย]))
                    วิธีแก้ปัญหาเวลาเขียน M Code แบบ each ซ้อนกันหลายอัน 571

                    และนี่คือแนวทางจัดการเวลามี each ซ้อนกันหลายชั้นครับ นั้นคือ ให้เปลี่ยน each แต่ละตัวเป็นชื่อ parameter เต็มๆ คู่กับ => แบบไม่ต้องย่อว่า each แบบปกตินั่นเอง

                  • เปิดให้ลงทะเบียนคอร์ส Public Live Online Training รุ่นที่ 1

                    เปิดให้ลงทะเบียนคอร์ส Public Live Online Training รุ่นที่ 1

                    คอร์สอบรมสด ที่สามารถเลือกได้

                    • Excel Level Up 6-7 ส.ค. (9:00-16:00)
                    • Power Query 3-4 ก.ย. (9:00-16:00)
                    • Power BI 1-2 ต.ค. (9:00-16:00)
                    เปิดให้ลงทะเบียนคอร์ส Public Live Online Training รุ่นที่ 1 572
                    เปิดให้ลงทะเบียนคอร์ส Public Live Online Training รุ่นที่ 1 573
                    เปิดให้ลงทะเบียนคอร์ส Public Live Online Training รุ่นที่ 1 574

                    ข้อมูลเกี่ยวกับการอบรม

                    1. การอบรมเป็นแบบ Live Online ผ่าน Zoom ดังนั้นผู้เรียนควรมีหน้าจอแยกออกมาอีกจอ นอกเหนือจาก computer ปกติเพื่อดูสิ่งที่ผมสอนด้วย (เช่น จอคอมแยก, จอ ipad, จอมือถือ เป็นต้น)
                    2. แต่ละวันจะอบรม 2 session คือ เช้า 9:00-12:00 และช่วงบ่าย 13:00-16:00 ในแต่ละช่วงจะมีพักเบรคย่อย 15 นาที ตามความเหมาะสม
                    3. การอบรมครั้งนี้ไม่อนุญาตให้มีการบันทึกวีดีโอนะครับ แต่ผมจะมีการตั้งกลุ่ม Line ให้สอบถามปัญหาหลังเรียนจบได้ภายใน 3 เดือนแทน ซึ่งในกลุ่มจะมีนักเรียนที่เข้าในรุ่นนี้เท่านั้น จึงมั่นใจว่าการตอบคำถามจะทำได้ทั่วถึงแน่นอน
                    4. ควรใช้คอมพ์ที่ใช้ Windows โดยยิ่งมี Excel version ยิ่งใหม่ยิ่งดี ดีสุดคือ Excel 365 แต่อย่างต่ำคือ Excel 2016 แต่ถ้าไม่มี ควรโหลด Power BI Desktop (ฟรี) มาใช้เรียน Power Query และ Power BI ด้วยนะครับ
                    5. เนื้อหาที่อบรมในครั้งนี้ จะคล้ายกับเนื้อหาในคอร์สออนไลน์ https://www.thepexcel.com/training-personal/ ถ้าใครเคยเรียนออนไลน์แล้วอาจจะได้รับความรู้ใหม่เพียงเล็กน้อย แต่การเรียนสดก็มีข้อดีคือการสอบถามปัญหาที่สงสัยได้สดๆ ทันทีครับ

                    ลงทะเบียน

                    https://forms.gle/AsTMUYY7ehTcsQVW8

                    เปิดให้ลงทะเบียนคอร์ส Public Live Online Training รุ่นที่ 1 575
                  • กราฟแสดงค่าพลังว่าคุณมี Skill Excel / Power BI แค่ไหน?

                    กราฟแสดงค่าพลังว่าคุณมี Skill Excel / Power BI แค่ไหน?

                    ลองเลื่อน ขีดพลังเล่นดู ถ้าพอใจก็ Print Screen แล้ว Save ออกไปเป็นรูปภาพได้เลยครับ

                  • วิธีจัดการเลขไทยใน Excel และ Power BI

                    วิธีจัดการเลขไทยใน Excel และ Power BI

                    ในช่วงนี้มีประเด็นในโลก Social เกี่ยวกับ “การใช้เลขไทยในเอกสารราชการ” ทำให้อ่านยาก ลามไปที่ถึงที่มาของเลขไทยและวัฒนธรรมไทยอันดีงาม (ไปได้ไงเนี่ย…) ซึ่งในที่นี้ผมจะไม่ขอพูดถึงเรื่องดราม่าอันนั้น แต่จะมาพูดถึงวิธีที่ Excel และ Power BI จัดการกับเลขไทยดีกว่า มาดูกันดีกว่าครับว่ามีอะไรน่าสนใจบ้าง?

                    เลขไทยจัดการไม่ยากอย่างที่คิด(ถ้าทำงานในฐานะตัวเลข)

                    ถ้าเราพิมพ์ตัวเลขไทย หรือ Copy เลขไทยลงไปใน Excel เองเลย ปกติ Excel จะมองอันนั้นเป็นตัวเลขอยู่แล้วนะครับ เช่น ถ้าพิมพ์เลขไทย ๑๒๓๔ เนี่ย excel จะมองว่าค่าที่แท้จริงของมันคือ 1234 อยู่แล้ว ดังนั้นไม่ใช่ปัญหา (ไม่ว่า Region ใน Control Panel จะตั้งค่าเป็น Thai หรือ US/UK ก็ตาม)

                    วิธีจัดการเลขไทยใน Excel และ Power BI 576

                    ถ้ามองว่าค่าที่แท้จริงเป็น 1234 แปลว่า ที่เราเห็นเป็นเลขไทย มันเป็นแค่ Format หรือรูปลักษณ์ภายนอก ดังนั้นวิธีแก้ไข คือ แค่เราปรับ Format ให้มันเป็นเลขอารบิกปกติ เช่น ปรับให้กลายเป็น General หรือ Number ก็จบ

                    วิธีจัดการเลขไทยใน Excel และ Power BI 577
                    ตอนยังมี Number Format เป็นเลขไทย
                    วิธีจัดการเลขไทยใน Excel และ Power BI 578
                    ตอนปรับ Number Format ช่องเดิมเลย ให้กลายเป็นเลขปกติ

                    ในทางกลับกันก็แปลว่า ถ้าเดิมเราทำงานกับเลขอารบิกอยู่ เราก็สามารถแปลงให้เห็นเป็นเลขไทยได้เช่นกัน ด้วยการใส่ Number format ที่เหมาะสม ใน Custom โดยมี Code [$-th-TH,D00] นำหน้า แล้วตามด้วย Custom Format มาตรฐาน เช่น

                    ถ้าอยากได้เลขจำนวนเต็มก็แบบนี้

                    [$-th-TH,D00]0

                    ถ้าอยากมี ทศนิยม 2 ตำแหน่ง ก็ใช้อันนี้

                    [$-th-TH,D00]0.00

                    ถ้าอยากมี Comma คั่นหลักพัน ก็ใช้อันนี้

                    [$-th-TH,D00]#,##0
                    วิธีจัดการเลขไทยใน Excel และ Power BI 579

                    จะเห็นว่าเราใช้การปรับ Format ได้อย่างอิสระ ดังนั้นข้าราชการทั้งหลายไม่จำเป็นต้องไปนั่งพิมพ์เลขไทยแต่แรกนะ ยากจะตาย!!

                    ข้อควรระวัง!

                    อย่างไรก็ตาม ถ้าเราพิมพ์เลขไทยในฐานะที่เป็นข้อความ เช่น พิมพ์เป็นส่วนหนึ่งของข้อความไปเลย อันนี้มันจะเก็บข้อมูลในฐานะข้อความจริงๆ ซึ่งการจะมาแก้เป็นเลขอารบิกภายหลังจะยุ่งหน่อย คือ ต้องใช้หลักการแทนที่อักขระ ๐๑๒๓๔๕๖๗๘๙ แต่ละตัวด้วย 0123456789 ซึ่งทำได้หลายวิธี เช่น

                    • ซึ่งจะใช้ find/replace ทีละตัว 10 ครั้ง
                    • ใช้ฟังก์ชัน SUBSTITUTE ทีละตัว 10 ครั้ง
                    • จะใช้มาโคร/VBAช่วย
                    • ถ้าใครมี Excel 365 จะเขียนฟังก์ชันด้วย LAMBDA มาจัดการก็ได้
                    • แต่ในบทความนี้ผมจะใช้การเขียนฟังก์ชันใน Power Query มาช่วยจัดการซึ่งอ่านได้ในตอนท้ายของบทความครับ (เพราะวิธีนี้ใช้กับ Power BI ได้ด้วย)

                    การ Save / เปิด ไฟล์เป็น Text, CSV ที่มีเลขไทย

                    ถ้าสมมติเรามีเลขไทยใน Excel ได้ตามต้องการแล้ว เช่น

                    วิธีจัดการเลขไทยใน Excel และ Power BI 580

                    จากนั้นหากเราต้องการ Save เป็นไฟล์ Text, CSV ก็ทำได้ เช่น แบบนี้ (เลือกเป็น CSV UTF-8)

                    วิธีจัดการเลขไทยใน Excel และ Power BI 581

                    ซึ่งในไฟล์ csv ถ้าลองไปเปิดใน notepad ก็จะเห็นบันทึกข้อมูลมาเป็นแบบนี้

                    วิธีจัดการเลขไทยใน Excel และ Power BI 582

                    จากนั้นในอนาคต เวลาเราดับเบิ้ลคลิ๊กเปิดไฟล์ CSV อันนั้นขึ้นมา (มันจะใช้ Excel เปิดอยู่แล้ว) ก็จะไม่มีปัญหา เราจะเห็นเป็นเหมือนเดิมเลย แค่ไม่มี format ตัวหนา/สี แต่ก็ยังเห็นเลขไทย ที่มีค่าที่แท้จริงเป็นตัวเลขถูกต้อง

                    วิธีจัดการเลขไทยใน Excel และ Power BI 583

                    แต่ถ้ากรณีเราดับเบิ้ลคลิ๊กเปิดไฟล์ CSV ที่ได้มาจากคนอื่นที่ Save แล้วดันเป็นภาษามนุษย์ต่างดาวแบบนี้ แสดงว่ามันใช้ Encoding ผิดตัวในการตีความอักขระในไฟล์นั้นๆ อย่าเพิ่งตกใจ… ถ้าใช้ Excel 2016 ขึ้นไปให้ใช้ Power Query เปิดก็ได้ แล้วเลือก Encoding ให้ถูกต้อง

                    วิธีจัดการเลขไทยใน Excel และ Power BI 584

                    การใช้ Power Query ใน Excel จัดการเลขไทยแบบง่าย

                    เลือก Get Data จาก Text,CSV ที่มีปัญหาซะ

                    วิธีจัดการเลขไทยใน Excel และ Power BI 585

                    เลือก Encoding ที่อ่านภาษาไทยออก ให้ลอง 65001:UTF-8 กับ 874:Thai (Windows) ดู ว่าอันไหนอ่านออกบ้างมั้ย? ในที่นี้ UTF-8 อ่านออก จากนั้นกด Transform Data เพื่อจัดการหัวตารางให้เรียบร้อย

                    วิธีจัดการเลขไทยใน Excel และ Power BI 586

                    ลบ Step Change Type ออก แล้วกด Use First Row as Header ใหม่อีกที จะได้ผลลัพธ์แบบนี้

                    วิธีจัดการเลขไทยใน Excel และ Power BI 587

                    จะเห็นว่าคอลัมน์จำนวน มันตีความเลขไทยเป็นข้อความ (ABC) ซึ่งจริงๆ ไม่ถูกต้อง แต่การแก้ใน Power Query จะค่อนข้างยุ่ง ดังนั้นเราจะโหลดผลลัพธ์ออกไปใน Excel เลย แล้วไปจัดการใน Excel ดังนั้นให้กด Close & Load to… แล้วออกมาเป็น Table ใน Excel เราจะพบว่า Excel ก็จะมองเลขไทยเป็น Text ไปด้วย

                    วิธีจัดการเลขไทยใน Excel และ Power BI 588

                    แต่วิธีแก้ใน Excel ง่ายมาก แค่สร้างคอลัมน์ใหม่ข้างๆ แล้ว เอาคอลัมน์เลขไทยไปคูณ 1 เพื่อบังคับให้เป็นตัวเลขก็จบเลย

                    วิธีจัดการเลขไทยใน Excel และ Power BI 589

                    วิธีจัดการเลขไทยใน Power Query / Power BI แบบจบในตัว

                    ในกรณีที่เราต้องไปทำรายงานใน Power BI เราต้องจัดการให้จบใน Power Query เลย ซึ่งขั้นตอนถ้าจะทำด้วย Replace Value 10 รอบก็ได้ แต่ก็จะค่อนข้างยุ่งยาก ดังนั้นผมจึงเขียนฟังก์ชันขึ้นมาจัดการเรื่องนี้ให้แล้วครับ ชื่อว่า ThepThaiNumbertoArabic (ไปโหลดฟังก์ชันนี้ และฟังก์ชันอื่นๆ ที่น่าสนใจมากมายได้จากที่นี่)

                    ถ้าใครอยากรู้ว่าในฟังก์ชันมีอะไร ก็จะประมาณนี้ครับ

                    (OriginalText as text) as text =>
                    let
                        TextList=Text.ToList(OriginalText),
                        ZipReplace=List.Zip({{"๐".."๙"},{"0".."9"}}),
                        Result=List.ReplaceMatchingItems(TextList,ZipReplace,Comparer.OrdinalIgnoreCase),
                        ResultCombine=Text.Combine(Result)
                    in
                        ResultCombine

                    แค่มีฟังก์ชันของผมและเรียกใช้ฟังก์ชันนั้นกับคอลัมน์ที่ต้องการด้วย Invoke Custom Function ก็จะใช้ได้เลย

                    วิธีจัดการเลขไทยใน Excel และ Power BI 590
                    วิธีจัดการเลขไทยใน Excel และ Power BI 591

                    กรณีที่ต้องการ Convert เป็นตัวเลข ก็จะสามารถกด Change Type อีกทีได้ตามปกติแล้ว

                    วิธีจัดการเลขไทยใน Excel และ Power BI 592

                    ดังนั้นเพื่อนๆ น่าจะเห็นแล้วว่า กรณีที่เราได้ข้อมูลมาเป็นเลขไทยจริงๆ (แล้วเราไม่ชอบ) เราก็สามารถจัดการให้มันเป็นเลขอารบิกได้โดยไม่ยากเกินไปนัก หวังว่า Post นี้จะมีประโยชน์กับเพื่อนๆ นะครับ ^^

                  • Principle of DAX : หลักการทำงานที่แท้จริงของ DAX ใน Power BI และ Excel

                    Principle of DAX : หลักการทำงานที่แท้จริงของ DAX ใน Power BI และ Excel

                    บทความนี้เป็นบทความที่ผมตั้งใจทำมากๆ เพื่อที่จะช่วยให้เพื่อนๆ ที่ศึกษา DAX ไม่ว่าจะเป็นใน Data Model ของ Power BI หรือ Excel ก็ตาม ได้เข้าใจการทำงานของมันให้แม่นยำและลึกซึ้งมากขึ้น เพราะหลายครั้งผมได้เจอคนใช้งาน Power BI ที่เหมือนจะเข้าใจผิดหรือไม่เข้าใจในการทำงานของมันจริงๆ ซึ่งจะยิ่งแย่เข้าไปใหญ่หากมีการนำความรู้ที่ไม่ถูกต้องไปถ่ายทอดต่ออีก (ไม่แปลกถ้าแรกๆ จะเข้าใจผิด ผมก็เคยมีหลายอย่างที่เข้าใจผิดกับ DAX มาก่อน แต่ถ้าเราเข้าใจได้ถูกต้องมันย่อมดีกว่าเนอะ)

                    DAX มีความคล้ายกับฟังก์ชันใน Excel มากๆ แต่ก็มีหลายฟังก์ชันที่ไม่เหมือนกัน และหลักการทำงานก็ไม่ค่อยเหมือนกันด้วยนะ…

                    การทำงานของ DAX นั้นมีหลักการพื้นฐานที่สำคัญมากๆ อยู่ที่คำ 2 คำ นั่นคือ Row Context และ Filter Context ซึ่งถ้าหากเราไม่แม่นเรื่องนี้ เราจะงงมากๆ เวลาเจอการทำงานของ DAX ที่เริ่มจะมีความซับซ้อนมากขึ้น (อย่าลืมว่า DAX เป็นภาษาที่สามารถเขียนได้ซับซ้อนมากๆ เช่น สูตรเดียวยาว 20-30 บรรทัดได้ ตัวอย่างเช่นในเว็บนี้ )

                    เราเขียน DAX ได้ที่ไหนบ้าง?

                    • New Measure : เป็นสูตรคำนวณที่เอาไว้ใส่ใน Values ของ Visual ใน Report มักเป็นการสรุปข้อมูล เช่น SUM, AVERAGE, DISTINCTCOUNT ซึ่งผลลัพธ์ของ Measure จะต้องเป็นข้อมูลที่มีค่าเดียว แต่จะเป็นตัวเลข หรือ ข้อความก็ได้ แต่จะเป็นตารางหรือ ข้อมูลที่มีหลายค่าไม่ได้
                      • New Quick Measure (มีใน Power BI เท่านั้น) : ให้ Power BI ช่วยสร้าง Measure ให้แบบอัตโนมัติจากรูปแบบที่โปรแกรมเตรียมไว้ เช่น Year to Date Summary
                    • New Column ใน ตารางข้อมูล : เป็นการเขียนสูตรเพื่อสร้างคอลัมน์เพิ่มในตารางข้อมูล มักจะใช้เพื่อเอาไว้ลากลงส่วน Category/Axis ของ Visual ใน Report (หรือใช้ทดข้อมูลก่อนสร้าง Measure ก็ได้)
                    • New Table (มีใน Power BI เท่านั้น) : ไว้สร้างตารางขึ้นมาใหม่ด้วยสูตร เช่น Date Table

                    วิธีเขียน DAX เพื่ออ้างอิงสิ่งต่างๆ

                    • อ้างอิง Table : อ้างอิงด้วย TableName
                    • อ้างอิง Column : อ้างอิงด้วย TableName[ColumnName]
                    • อ้างอิง Measure : อ้างอิงด้วย [MeasureName]
                      • สาเหตุที่เราไม่ใส่ชื่อตารางหน้า Measure เพราะ จะได้ไม่สับสนกับชื่อคอลัมน์ และ ตารางที่ Measure อยู่ก็ไม่มีผลกับการคำนวณ

                    จากนั้นเรามาดูความหมายของ Row Context และ Filter Context แบบเบื้องต้นกันก่อนครับ

                    Row Context vs Filter Context เบื้องต้น

                    Row Context ขั้นต้น

                    เป็นการพิจารณาว่าสูตรกำลังคำนวณบรรทัดไหนของตารางอยู่? ซึ่งสูตรมันก็จะสามารถดึงค่าเฉพาะแถวนั้นๆ ในคอลัมน์ที่ต้องการได้

                    ซึ่งจะเกิดขึ้นโดยมีการคำนวณทีละ Row ของ Table คล้ายๆ การวน Loop ทีละบรรทัดของตารางจนครบ เช่น การเพิ่ม New Column โปรแกรมก็ต้องคำนวณค่าของแต่ละแถวออกมา หรือ การใช้ฟังก์ชันจำพวก iterator เช่น SUMX, FILTER ก็ต้องพิจารณาทีละแถวเป็นต้น (เดี๋ยวจะพูดถึงภายหลัง)

                    ตัวอย่างที่เข้าใจง่ายสุด คือ การ เพิ่ม New Column ชื่อว่า Revenue โดยจะคำนวณยอดขาย จาก คอลัมน์ Unit Price และ Quantity ในตาราง OrderDetail ซึ่งเราจะใส่สูตรแบบนี้

                    Revenue = OrderDetail[Quantity]*OrderDetail[Unit Price]
                    Principle of DAX : หลักการทำงานที่แท้จริงของ DAX ใน Power BI และ Excel 593

                    จะเห็นว่า แม้จะเขียนสูตรอ้างอิงคอลัมน์ทั้งคอลัมน์คูณกัน แต่ว่าค่าผลลัพธ์ของ Revenue ในแต่ละแถวของตารางนั้นสามารถได้ผลลัพธ์ออกมาเป็นค่าเดียวได้ (แถมค่าเป็นของแถวใครแถวมัน) สาเหตุเป็นเพราะมี Row Context อยู่นั่นเอง

                    หากว่าเราดันไปเขียนสูตรเดียวกันนี้เลยแต่ใส่ลงใน Measure มันก็จะไม่ Work… เพราะว่า Measure นั้นเป็นสิ่งที่ต้องได้ผลลัพธ์เป็นค่าเดียว เพื่อเอาไปแสดงใน Value การที่มันไม่สามารถคำนวณค่าเดียวออกมาได้ เพราะใน Report ไม่มี Row Context อยู่นั่นเอง ทำให้สูตรไม่สามารถแสดงค่าเดียวออกมาได้ (แค่จะเขียนว่า = OrderDetail[Quantity] เฉยๆ ยังไม่ได้เลย)

                    Principle of DAX : หลักการทำงานที่แท้จริงของ DAX ใน Power BI และ Excel 594

                    เอาล่ะ เราน่าจะพอเห็นภาพของ Row Context เบื้องต้นกันไปแล้ว เดี๋ยวไปทำความรู้จัก Filter Context กันก่อน

                    Filter Context ขั้นต้น

                    เป็นการพิจารณาว่ามีเงื่อนไขการ Filter อะไรบ้าง ในตำแหน่งที่สูตรกำลังคำนวณอยู่

                    ซึ่งการพิจารณา Filter จะเกิดขึ้นในหน้า Report นั่นเอง ซึ่งเราต้องพิจารณา Filter ทั้งในส่วนประกอบ Category/Axis ของ Visual นั้นๆ และทั้ง Visual/Page/Report Filter ใน Filter Pane รวมถึง Interaction ที่มาจาก Visual อื่นด้วย

                    ถ้าให้เข้าใจง่ายที่สุด หากผมเขียน Measure เพื่อจะคำนวณยอดขายรวม ดังนี้

                    Total Revenue = SUM(OrderDetail[Revenue])

                    หากผมลากลงไปใน Visual มันจะคำนวณค่าได้ถูกต้อง ตาม Category/Axis ของ Visual นั้นๆ แม้ทุกช่องจะคำนวณด้วยสูตรเดียวกัน ทั้งนี้เพราะแต่ละตำแหน่งมี Filter Context ไม่เหมือนกันนั่นเอง

                    Principle of DAX : หลักการทำงานที่แท้จริงของ DAX ใน Power BI และ Excel 595

                    อย่างเช่นในรูปข้างบน ตรง Brand Contoso ทวีป Asia ได้ยอด TotalRevenue เป็น 163,171,265 เป็นผลมาจากการที่มี Filter Context ซึ่งเปรียบเสมือนว่ามันทำการ Filter ข้อมูลให้ Brand เป็น Contoso และ Store Continent เป็น Asia (ซึ่งแม้จะอยู่คนละตารางกัน แต่ผลของมันวิ่งผ่าน Relationship ใน Data Model มา Filter ตาราง Order Detail ต่อได้) ทำให้คอลัมน์ Revenue มีจำนวนแถวลดลงก่อนจะทำการ SUM นั่นเอง

                    แต่ถ้าเราพยายามจะมาเขียนสูตร SUM Revenue ใน New Column ของตาราง มันจะได้ผลลัพธ์ที่น่าประหลาดใจ นั่นก็คือเราจะได้ค่าเท่ากันหมดเลย! (ซึ่งเป็นผลรวมของคอลัมน์ Revenue ทั้งตารางโดยไม่มีการ Filter ใดๆ) ทั้งนี้เป็นเพราะส่วนของตาราง Data จริงๆ นั้นมันไม่มี Filter Context นั่นเอง

                    Principle of DAX : หลักการทำงานที่แท้จริงของ DAX ใน Power BI และ Excel 596

                    หรือจะไปทำที่ตารางอื่น เช่น ตาราง Product (Dimension Table) แล้วคำนวณ Revenue จาก FactTable ก็ไม่ Work เช่นกันนะ

                    Principle of DAX : หลักการทำงานที่แท้จริงของ DAX ใน Power BI และ Excel 597

                    Row Context vs Filter Context ขั้นกลาง

                    หลังจากที่เราได้เริ่มรู้จัก Row Context และ Filter Context แบบเบื้องต้นกันไปแล้ว เดี๋ยวเราจะมาเรียนรู้ในระดับกลางๆ กันบ้าง

                    Row Context ขั้นกลาง

                    โดยผมจะขอเริ่มที่ Row Context ในกรณีที่ไม่ได้อยู่ที่ New Column แต่จะอยู่ในฟังก์ชันพวก Iterator อย่างตระกูล …X ทั้งหมดเช่น SUMX, AVERAGEX, CONCATENATEX (และอีกมากมาย) รวมถึงฟังก์ชันอย่าง FILTER ก็มี Row Context ด้วยเช่นกัน

                    สมมติว่าผมต้องการจะคำนวณ Measure เรื่อง Total Revenue โดยไม่ได้สร้างคอลัมน์ Revenue มาก่อน อันนี้ถ้าเราใช้ SUM ธรรมดาแล้วจะเอาสองคอลัมน์คูณกันในนั้นเลย มันจะไม่ Work เพราะ SUM มันอ้างอิงได้แค่คอลัมน์เดียวเท่านั้น

                    Total Revenue Wrong2 = SUM(OrderDetail[Quantity]*OrderDetail[Unit Price])
                    Principle of DAX : หลักการทำงานที่แท้จริงของ DAX ใน Power BI และ Excel 598

                    ครั้งจะ SUM แต่ละก้อนแล้วจับคูณกัน มันก็จะผิดอีก เพราะไม่ใช่สิ่งที่เราต้องการ…

                    ดังนั้นเราก็ต้องมีความรู้เพิ่มว่า เราสามารถใช้ SUMX มาช่วยในกรณีนี้ได้ ซึ่งมี Syntax ดังนี้

                    SUMX ( <Table>, <Expression> )

                    โดยเจ้า SUMX จะมองเข้าไปในแต่ละแถวของ <Table> ที่เราระบุ จากนั้น ในแต่ละแถวมันจะคำนวณตาม <Expression> ที่เราระบุ แล้วนำค่า Expression ของทุกแถวที่คำนวณได้มาหาผลรวมอีกที ซึ่งใน <Expression> ของ SUMX นี่แหละที่มี Row Context ตอนมันวน Loop เพื่อคำนวณแต่ละแถวใน <Table> ครับ ดังนั้นใน Expression เราจึงสามารถอ้างคอลัมน์คูณกันโดยได้ค่าของแต่ละแถวนั้นๆ ออกมาได้เลย แม้จะเขียนใน Measure ก็ตาม

                    Total Revenue SUMX = SUMX(OrderDetail,OrderDetail[Quantity]*OrderDetail[Unit Price])
                    Principle of DAX : หลักการทำงานที่แท้จริงของ DAX ใน Power BI และ Excel 599

                    หรือแม้แต่ตอนที่เราทำการคัดกรองข้อมูลด้วยฟังก์ชัน FILTER มันก็มี Row Context ใน <FilterExpression> เช่นกัน

                    FILTER ( <Table>, <FilterExpression> )

                    สมมติผมสร้าง New Table ด้วยสูตร FILTER ก็สามารถเขียนสูตรแบบนี้ได้ (เอาสูตรยาวๆ ไปใส่ใน https://www.daxformatter.com/ จะได้จัดย่อหน้าได้สวยๆ ดูง่ายขึ้น)

                    RichMaleOrder = 
                    FILTER (
                        OrderDetail,
                        ( OrderDetail[Unit Price] * OrderDetail[Quantity] ) > 250000
                            && RELATED ( Customer[Gender] ) = "M"
                    )

                    ซึ่ง FILTER จะมองเข้าไปในแต่ละแถวของตาราง OrderDetail แล้วดึงมาเฉพาะแถวที่ <FilterExpression> เป็นจริงเท่านั้น โดยความหมายในสูตรที่เขียนคือ ให้เอาเฉพาะแถวที่ Unit Price*Quantity ได้มากกว่า 250000 และ เพศลูกค้าเป็นผู้ชาย

                    ซึ่งใน <FilterExpression> นี่แหละที่มี Row Context เราจึงสามารถอ้างอิงคอลัมน์ในตาราง OrderDetail ได้เลยโดยตรง แต่ถ้าจะอ้างอิงตารางอื่น เราจะต้องใช้ RELATED มาช่วย

                    Filter Context ขั้นกลาง

                    สำหรับลูกเล่นในระดับกลางของการเล่นกับ Filter Context ก็คือ การใช้ CALCULATE เพื่อทำการเปลี่ยนแปลง Filter Context นั่นเอง

                    CALCULATE ( <Expression> , [<Filter1>] , [<Filter2>],... )

                    ความสามารถของ CALCULATE โดยหลักการคือสามารถเปลี่ยน Filter Context ได้ก่อนจะคำนวณ Expression ที่ระบุ

                    ตัวอย่างง่ายๆ เลยของการใช้ CALCUCATE เพื่อเปลี่ยน Filter Context คือแบบนี้

                    Total Revenue Asia = CALCULATE([Total Revenue],Store[Store Continent]="Asia")

                    ความหมายคือ ก่อนจะคำนวณ [Total Revenue] ออกมา ให้ทำการเปลี่ยน Filter ของ Store[Store Continent] ให้เป็น Asia ซะ

                    Principle of DAX : หลักการทำงานที่แท้จริงของ DAX ใน Power BI และ Excel 600

                    ซึ่งจากผลลัพธ์ข้างบนจะเห็นว่า Filter อื่นๆ เช่น Brand จะยังคงทำงานอยู่ตามปกติ แต่ Filter เรื่อง Store[Store Continent] ที่เดิมเคยเป็นทวีปอื่น เช่น Europe ก็ได้ถูกเปลี่ยนให้กลายเป็น Asia ไปซะแล้ว ค่าก็เลยเปลี่ยนจากปกติที่ควรจะเป็น 159,023,205 ไปเป็น 163,171,265 ซึ่งเป็นค่าของทวีป Asia นั่นเอง

                    แล้วมีอะไรอีก?

                    ซึ่งจริงๆ แล้ว CALCULATE สามารถเปลี่ยน Filter Context ได้หลากหลายแบบมาก เช่น

                    • ปลด Filter ด้วย ALL, REMOVEFILTERS, ALLSELECTED, ALLEXCEPT
                    • การใส่ Filter คืนกลับ (หลังปลด) ด้วย DISTINCT, VALUE
                    • การใช้งานร่วมกับฟังก์ชันกลุ่ม Time Intelligence
                    • ปรับเปลี่ยนวิธีทำงานของ Relationship ก่อนจะ Filter ก็ได้ เช่น USERELATIONSHIP, CROSSFILTER
                    • ทำการเปลี่ยน Row Context ให้กลายเป็น Filter Context ด้วยเทคนิคที่เรียกว่า Context Transition
                    • การนำ Context Transition ไปใช้ใน Iterator เช่น SUMX, MAXX, MINX, CONCATENATEX, RANKX
                    • การสร้างและใช้ตารางจำลองเพื่อเป็นเงื่อนไขใน CALCULATE
                    • CALCULATETABLE คู่แฝดของ CALCULATE
                    • การทำงานของ Expanded Table

                    เรื่องพวกนี้ก็จะเป็นเรื่องที่เป็นเนื้อหาขั้นสูงขึ้นอีก ซึ่งในบทความนี้ผมจะขอแนะนำแค่ Context Transition ก่อนแล้วกันครับ

                    Context Transition คืออะไร?

                    กลับมาที่ผมบอกว่าในตาราง Data นั้นไม่มี Filter Context มีแต่ Row Context เวลาเราเพิ่มคอลัมน์ใหม่แล้ว SUM เลยได้ค่าเดียวกันหมด เช่น รูปเดิมของเราอันนี้

                    Principle of DAX : หลักการทำงานที่แท้จริงของ DAX ใน Power BI และ Excel 597

                    แต่ถ้าเราใช้ CALCULATE ไปครอบสูตรอันเดิมของเราซะ ผลลัพธ์จะกลายเป็น Total Revenue ของแต่ละ Product ได้อย่างถูกต้องเฉยเลย

                    Total Revenue Context = CALCULATE(SUM(OrderDetail[Revenue]))
                    Principle of DAX : หลักการทำงานที่แท้จริงของ DAX ใน Power BI และ Excel 602

                    ทั้งนี้เพราะมันเกิดสิ่งที่เรียกว่า Context Transition ขึ้น ซึ่งมันจะเปลี่ยน Row Context ที่มองเห็นทั้งหมดให้กลายเป็น Filter Context หรือพูดง่ายๆ คือ แต่ละค่าในแถวนั้นๆ จะถูกทำให้กลายเป้นเงื่อนไขในการ Filter นั่นเอง

                    แล้ว Filter นั้นก็จะส่งผลไปที่ตาราง OrderDetail จนทำให้เกิดผลรวมของแต่ละ Product นั้นๆ ได้อย่างถูกต้อง!

                    ที่มันเจ๋งกว่านั้นอีก คือ ถ้าเราใส่สูตรโดยอ้างอิง Measure โดยตรงไปเลย มันจะได้ค่าแบบเดียวกับที่มี CALCULATE ครอบเลย

                    Total Revenue Context = [Total Revenue]
                    Principle of DAX : หลักการทำงานที่แท้จริงของ DAX ใน Power BI และ Excel 603

                    สาเหตุเป็นเพราะการอ้างอิง Measure ใน DAX นั้น ในความเป็นจริงโปรแกรมจะแอบเอา CALCULATE มาครอบให้เสมอนั่นเอง มันจึงเกิด Context Transition ขึ้นโดยที่เราอาจจะไม่ได้ตั้งใจด้วยซ้ำ!! ซึ่งถ้าใครใช้ประโยชน์จากมันได้ก็จะสามารถเขียน DAX เพื่อแก้ปัญหาต่างๆ ได้สั้นลงมาก ซึ่งถ้าใครสนใจเทคนิคพวกนี้ รวมถึงเทคนิคอื่นๆ ที่ผมยังไม่ได้พูดถึง สามารถไปศึกษาหาความรู้เพิ่มเติมได้

                    แต่ถ้าไม่รู้จะหาความรู้จากไหน ผมมีคอร์สสอนเรื่องนี้อยู่แล้วครับ 2 คอร์ส คือ Power BI Basic กับ คอร์ส Advance ที่เน้น DAX แบบเน้นๆ เลย (ขอปิดท้ายด้วยการขายของหน่อย 555)

                    คอร์สออนไลน์ Powerful Data with Power BI การวิเคราะห์และนำเสนอข้อมูลขั้นเทพ

                    • คุณจะได้เรียนรู้เครื่องมือ Power BI ซึ่งเป็นเครื่องมือ Business Intelligent ชั้นยอดจากค่าย Microsoft
                    • ใช้ทำ Interactive Dashboard ที่มี Visual ที่เจ๋งกว่า Excel หลายเท่า
                    • มีความสามารถในการแชร์ Dashboard ไปให้คนอื่นใช้ได้ง่าย/ปลอดภัย
                      ในคอร์สนี้คุณจะได้เรียนรู้ทั้ง 3 แกนหลักของโปรแกรมนี้ ทั้ง
                      • การ Get/Transform Data ด้วย Query Editor (เนื้อหาน้อยกว่าในคอร์ส Power Query)
                      • การทำ Data Model และเขียนสูตร DAX ในระดับที่ใช้งานทั่วไป
                        • เรียนรู้ Data Model แบบพื้นฐาน
                        • รองรับการทำ Data Actual vs Budget ได้
                        • สูตร DAX จะสอนนี้ก็จะลงลึกถึง CALCULATE และ Time Intelligent
                      • การสร้าง Report ด้วยการสร้างกราฟและปรับแต่ง Visual ต่างๆ รวมถึงการทำให้เกิดความ Interactive มากขึ้น
                    • ราคา 2,290 บาท (อัปเดทเนื้อหาให้ฟรีทุกปี)

                    Power BI DAX Advance

                    เพื่อให้สามารถเขียนสูตร DAX ที่มีความซับซ้อนมากขึ้น เพื่อตอบโจทย์ทางธุรกิจได้ตรงจุดมากขึ้น
                    สามารถต่อยอดความรู้ด้วยการอ่านเนื้อหายากๆ จากเว็บ https://www.daxpatterns.com/ ได้
                    เน้นเรื่องการเขียน DAX โดยเฉพาะ โดยมีทฤษฎีขั้นสูงมากมาย เช่น

                    • Context Transition
                    • Date Table & Time Intelligence
                    • DAX กับการปรับ Data Model
                    • Data Lineage และ TREATAS
                    • Concept ของ Expanded Table
                    • การสร้าง Virtual Table
                    • การใช้ DAX Studio เบื้องต้น / Calculation Group

                    มีตัวอย่างเคสมากมาย เช่น

                    • ABC Analysis
                    • การคำนวณต้นทุนแบบ FIFO
                    • Same Store Sales Growth
                    • แบ่ง Segment ด้วย RFM Analysis
                    • Semi-Additive ยอด Balance

                    ราคา 2,490 บาท (อัปเดทเนื้อหาให้ฟรีทุกปี)