Dynamic Time Warping อธิบายโดยใช้ Python และ HAR dataset

โหนดต้นทาง: 1123947

บทความนี้เผยแพร่โดยเป็นส่วนหนึ่งของไฟล์ Blogathon วิทยาศาสตร์ข้อมูล

บทนำเกี่ยวกับการตัดเวลาแบบไดนามิก

การจัดประเภทอนุกรมเวลาเป็นงานทั่วไปที่คุณจะมีข้อมูลจากโดเมนต่างๆ เช่น การประมวลผลสัญญาณ IoT กิจกรรมของมนุษย์ และอื่นๆ และจุดมุ่งหมายสูงสุดคือการฝึกโมเดลเฉพาะเพื่อให้สามารถทำนายคลาสของอนุกรมเวลาใดก็ได้ด้วย ความแม่นยำเกือบสมบูรณ์แบบ ชุดข้อมูลที่ระบุควรมีป้ายกำกับลำดับเวลาเพื่อให้โมเดลของเราสามารถทำนายคลาสของอนุกรมเวลาได้อย่างแม่นยำ

วิธีแก้ปัญหาแบบคลาสสิกวิธีหนึ่งสำหรับปัญหานี้คือการใช้วิธีการของอัลกอริทึมเพื่อนบ้านที่ใกล้ที่สุดของ K ในบทความนี้ เราจะข้ามผ่านวิธีการปกติของระยะทางแบบยุคลิดและเราจะใช้ การแปรปรวนของเวลาแบบไดนามิก or เมตริก DTW. วิธีนี้คำนึงถึงว่าเมื่อเราเปรียบเทียบอนุกรมเวลาสองชุดที่ต่างกัน อาจมีความยาวและความเร็วต่างกันไป

ภาพปก | การห่อเวลาแบบไดนามิก

วิธีการนี้ไม่ได้ซับซ้อนขนาดนั้นแต่เป็นสิ่งที่ค่อนข้างพิเศษเฉพาะสำหรับอัลกอริธึม DTW หรือ Dynamic Time Warping ข้อผิดพลาดทั่วไปบางประการอาจรวมถึงการปรับแต่งไฮเปอร์พารามิเตอร์ในโมเดล KNN และมองข้ามส่วน DTW เพียงอย่างเดียว แต่ไม่ควรทำเช่นนั้น ข้อเสียที่สำคัญอย่างหนึ่งของอัลกอริธึมคือความซับซ้อนของเวลาเพิ่มขึ้นเกือบเท่าทวีคูณสำหรับชุดข้อมูลขนาดใหญ่ที่มีลำดับขนาดใหญ่ โมเดลดังกล่าวอาจไม่สามารถฝึกได้ในระยะเวลาที่เหมาะสม เราอาจต้องทำการปรับแต่งเพิ่มเติมในอัลกอริทึมเพื่อเพิ่มความเร็ว ซึ่งเราจะพูดถึงในภายหลัง

การแปรปรวนของเวลาแบบไดนามิกทำงานอย่างไร

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

คนที่ลืมอดีตจะถูกประณามให้ทำซ้ำ” - George Santayana

ในบทความนี้ เราจะเน้นที่ส่วน Data Science ของอัลกอริทึมและใช้ python เป็นภาษาพื้นฐานของเราเพื่อทำความเข้าใจวิธีการทำงานของอัลกอริทึม เราจะใช้โมดูลห้องสมุดที่เรียกว่า “ระยะทาง” ซึ่งจะช่วยเราคำนวณระยะห่างระหว่างคลื่นไซน์สองคลื่นที่เฟสเปลี่ยนจากกัน

ในการติดตั้งแพ็คเกจให้ใช้รหัสต่อไปนี้:

!pip ติดตั้ง dtaidistance

สำหรับสภาพแวดล้อมของโน้ตบุ๊กหรือลบเครื่องหมายอัศเจรีย์หากคุณกำลังติดตั้งผ่าน command prompt

รหัสสินค้า:

จาก dtaidistance นำเข้า dtw จากการนำเข้า dtaidistance dtw_visualisation เป็น dtwvis จากการนำเข้าสุ่มตัวอย่าง นำเข้าจำนวนเป็น np x = np.arange(0, 20, .5) s1 = np.sin(x) s2 = np.sin(x - 1) = dtw.warping_path(s1, s2) dtwvis.plot_warping(s1, s2, path) ระยะทาง = dtw.distance(s1, s2)

Output:

ระยะการแปรปรวนที่เหมาะสมที่สุดระหว่างคลื่นไซน์ 2 คลื่น

รูปด้านบนแสดงระยะการแปรปรวนที่เหมาะสมที่สุดระหว่างคลื่นไซน์ 2 คลื่น

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

รหัสสินค้า:

d เส้นทาง = dtw.warping_paths(s1, s2, window=20) best_path = dtw.best_path(เส้นทาง) dtwvis.plot_warpingpaths (s1, s2, เส้นทาง, best_path)

Output:

The Cost Matrix | การห่อเวลาแบบไดนามิก

เมทริกซ์ต้นทุน

เมทริกซ์ประเภทนี้ยังถูกสร้างขึ้นซึ่งใช้อัลกอริทึมอื่นที่เรียกว่า อัลกอริทึม Needleman-Wunsch. อัลกอริธึมนี้ยังใช้เพื่อจัดตำแหน่งโปรตีนและลำดับนิวคลีโอไทด์ในชีวสารสนเทศ นอกจากนี้ ยังใช้อัลกอริธึมอื่นซึ่งก็คือ ระยะทางเลเวนชไตน์. อัลกอริธึมทั้งสองนี้อยู่ในการจำแนกประเภทอัลกอริธึมตระกูล Dynamic Programming

ในรูปที่สอง การโทรแต่ละครั้งเป็นตัวเลขที่แสดงระยะทางของจุดข้อมูลสองจุดตามลำดับที่นำมาเปรียบเทียบ จุดข้อมูลหนึ่งจุดสำหรับแต่ละลำดับ สีเข้มหมายถึงระยะทางที่ต่ำกว่าและเราสามารถดึงเส้นทางที่เหมาะสมที่สุดซึ่งถูกดึงออกมาในรูปแบบของเส้นสีแดงตามที่แสดงในภาพ ความซับซ้อนของเวลาคือ O(m,n) โดยที่ m และ n เป็นลำดับตามลำดับและมีต้นทุนกำลังสอง เป็นเรื่องปกติในโลกแห่งความเป็นจริงที่ซีเควนซ์มีขนาดใหญ่และโมเดล KNN ไม่ได้รับการปรับให้เหมาะสมอย่างสมบูรณ์ โมเดลโดยรวมอาจใช้เวลาพอสมควรในการฝึก

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

ในโค้ดที่แล้ว เราแสดงเมทริกซ์โดยใช้ฟังก์ชัน 'warping_paths()' ซึ่งโดยค่าเริ่มต้นจะใช้อาร์กิวเมนต์หนึ่งตัว 'use_pruning=True' และด้วยเหตุนี้ มุมบนขวาและล่างซ้ายจะถูกตัดแต่งเพื่อการเพิ่มประสิทธิภาพ ซึ่งหมายความว่าหลังจากเสียค่าใช้จ่ายไปแล้ว ค่าต่างๆ จะไม่ถูกคำนวณ เนื่องจากจะทำให้ใช้ CPU มาก และเราพบผลลัพธ์ที่ต้องการแล้ว ในโมดูลเวอร์ชันเก่าหรือเวอร์ชันก่อน 2.3.2 คุณต้องส่งพารามิเตอร์นี้ไปยังฟังก์ชันอย่างชัดเจน ตอนนี้เราจะย้ายไปยังสถานการณ์จริงเพื่อใช้แบบจำลองของเรา

กรณีศึกษาจริงเพื่อทำความเข้าใจการห่อเวลาแบบไดนามิก

การรับรู้กิจกรรมของมนุษย์หรือชุดข้อมูล HAR คือสิ่งที่เราจะใช้ที่นี่ และสามารถดาวน์โหลดได้จาก ห้องสมุด UCL ซึ่งมีข้อมูลอนุกรมเวลาที่ติดป้ายกำกับไว้ล่วงหน้า ข้อมูลนี้ถูกจับด้วยความช่วยเหลือของสมาร์ทโฟนที่ถือโดยมนุษย์ และสามารถจำแนกการกระทำออกเป็นกิจกรรมเฉพาะบางอย่างได้ เช่น นั่ง เดิน ยืน นอน เดิน_ชั้นล่าง หรือเดินขึ้นบันได ทั้งหมดนี้สามารถอนุมานได้จากความเร็วเชิงมุมและความเร่งเชิงเส้นซึ่งสามารถหาได้จากฮาร์ดแวร์ของสมาร์ทโฟน การสังเกตทุกครั้งคือข้อมูลที่ประกอบด้วยคุณสมบัติเวกเตอร์ 561 รายการพร้อมตัวแปรโดเมนเวลาและความถี่ และป้ายกำกับเฉพาะที่แสดงถึงกิจกรรมของบุคคลในขณะนั้น เป้าหมายของเราคือการสร้างแบบจำลองที่สามารถทำนายกิจกรรมของมนุษย์โดยใช้ข้อมูลจากสมาร์ทโฟน

รหัสสินค้า:

x_train_file = open(r'UCI HAR Dataset/train/X_train.txt', 'r') y_train_file = open(r'UCI HAR ชุดข้อมูล/train/y_train.txt', 'r') x_test_file = เปิด (r'UCI HAR ชุดข้อมูล/ทดสอบ/X_test.txt', 'r') y_test_file = เปิด (r'UCI HAR ชุดข้อมูล/ทดสอบ/y_test.txt', 'r') x_train = [] y_train = [] x_test = [] y_test = [] labels = {1:'WALKING', 2:'WALKING UPSTAIRS', 3:'WALKING DOWNSTAIRS', 4:'SITTING', 5:'STANDING', 6:'LAYING'} สำหรับ x ใน x_train_file: x_train.append( [float(ts) สำหรับ ts ใน x.split()]) สำหรับ y ใน y_train_file: y_train.append(int(y.rstrip('n'))) สำหรับ x ใน x_test_file: x_test.append([float(ts) สำหรับ ts ใน x.split()]) สำหรับ y ใน y_test_file: y_test.append(int(y.rstrip('n'))) x_train = np.array(x_train) y_train = np.array(y_train) x_test = np .array(x_test) y_test = np.array (y_test) สี = ['#D62728','#2C9F2C','#FD7F23','#1F77B4','#9467BD', '#8C564A','#7F7F7F' ,'#1FBECF','#E377C2','#BCBD27']
รหัสประจำตัว=0

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

รหัสที่ไม่มีการเพิ่มประสิทธิภาพ :

สำหรับ r ในช่วง (len(x_test)): ระยะทาง = dtw.distance(x_train[idx], x_test[r])

เวลาดำเนินการของเซลล์ :

เวลาเล่น: 25 นาที 16 วินาที

รหัสที่มีการเพิ่มประสิทธิภาพ :

สำหรับ r ในช่วง (len(x_test)): ระยะทาง = dtw.distance(x_train[idx], x_test[r], window=20, use_pruning='True')

เวลาดำเนินการของเซลล์ :

เวลาเล่น: 1 นาที 42 วินาที

อย่างที่คุณเห็นความแตกต่างที่สำคัญในแล็ปท็อปของฉันที่ใช้โน้ตบุ๊ก Jupyter บนชิปรุ่นที่ 5 ของ i8 ดังนั้น เราจะใช้อัลกอริทึม KNN กับ k=20 และขนาดหน้าต่าง 20 สำหรับฟังก์ชัน Dynamic Time Warping ตัวแปร 'idx' ช่วยในการจัดเก็บดัชนีของอนุกรมเวลาสำหรับชุดทดสอบของเรา

ตอนนี้เราจะกำหนดฟังก์ชันสำหรับแบบจำลองของเราและส่งคืนผลลัพธ์ตามจำนวนเพื่อนบ้านของ KNN ที่ให้มาและดัชนีของอนุกรมเวลาตามลำดับของชุดการทดสอบของเรา จากนั้นจะส่งคืนป้ายกำกับหนึ่งรายการซึ่งสามารถเป็น:

ป้าย = { WALKING, WALKING_UPSTAIRS, WALKING_DOWNSTAIRS, SITTING, STANDING, LAYING }

รหัสการทำงาน :

def classifyNN(k:int, idx:int) -> str: idxs=range(0,x_train.shape[0]) n=x_train.shape[0] distances=[] counters={} c=1; max_value=0 สำหรับ r ในช่วง (n): distances.append(dtw.distance(x_test[idx], x_train[idxs[r]],window=10,use_pruning=True)) NN=sorted(range(len(distances) )), คีย์=แลมบ์ดา i: ระยะทาง[i], ย้อนกลับ=เท็จ)[:k] สำหรับ l ใน labels.values(): ตัวนับ[l]=0 สำหรับ r ใน NN: l=labels[y_train[r]] counters[l]+=1 if (counters[l])>max_value: max_value=counters[l] #print('NN(%d) has label %s' % (c,l)) c+=1 # ป้ายชื่อด้วย คีย์ความถี่สูงสุด = [k สำหรับ k ในตัวนับถ้าตัวนับ [k] == max_value] # ส่งคืนตัวอย่างสุ่มหนึ่งตัวอย่างในกรณีที่มีการเสมอกันระหว่างหลายป้ายกำกับส่งคืน (sample(keys,1)[0]) 

กรณีทดสอบ

ในที่นี้เราจะสาธิตตัวอย่างกรณีทดสอบบางส่วน แต่คุณสามารถทดลองกับค่า 'idx' ใดก็ได้ และค้นหาป้ายกำกับที่เกี่ยวข้องสำหรับกรณีทดสอบนั้น

1. กรณีทดสอบครั้งแรก: ยืน

รหัสสำหรับแสดงข้อมูล :

k=20 idx=3 plt.plot(x_test[idx], label=labels[y_test[idx]], color=colors[y_test[idx]-1], linewidth=2) plt.xlabel('ตัวอย่าง @50Hz' ) plt.legend(loc='บนซ้าย') plt.tight_layout()

Output:

การแสดงภาพตัวแปรยืน

รหัสสำหรับค้นหาฉลากทดสอบ :

%%เวลา classifyNN(k,idx)

Output:

เวลาผนัง: 3 นาที 13 วินาที 'ยืน' 

2. กรณีทดสอบที่สอง: นั่ง

รหัสสำหรับแสดงข้อมูล :

k=20 idx=200 plt.plot(x_test[idx], label=labels[y_test[idx]], color=colors[y_test[idx]-1], linewidth=2) plt.xlabel('ตัวอย่าง @50Hz' ) plt.legend(loc='บนซ้าย') plt.tight_layout()

Output:

การแสดงภาพตัวแปรการนั่ง

รหัสสำหรับค้นหาฉลากทดสอบ :

%%เวลา classifyNN(k,idx)

Output:

เวลาผนัง: 3 นาที 15 วินาที 'นั่ง' 

3. กรณีทดสอบที่สาม: การเดิน

รหัสสำหรับแสดงข้อมูล :

k=20 idx=401 plt.plot(x_test[idx], label=labels[y_test[idx]], color=colors[y_test[idx]-1], linewidth=2) plt.xlabel('ตัวอย่าง @50Hz' ) plt.legend(loc='บนซ้าย') plt.tight_layout()

Output:

การแสดงภาพตัวแปรเดิน | การแปรปรวนของเวลาแบบไดนามิก

รหัสสำหรับค้นหาฉลากทดสอบ :

%%เวลา classifyNN(k,idx)

Output:

เวลาผนัง: 3 นาที 9 วินาที 'เดิน'

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

Parallelisation มีความเป็นไปได้สำหรับการปรับให้เหมาะสมของ Dynamic Time Wraping หรือไม่

คำถามทั่วไปต่อไปที่นี่คือเกี่ยวกับการทำให้ขนานกัน เนื่องจากเมื่อกรอบข้อมูล NumPy และ pandas ทำงานกับชุดข้อมูลขนาดใหญ่และต้องทำบางอย่างให้เสร็จในเวลาอันสั้น Spark ถูกสร้างขึ้นท่ามกลางเหตุผลอื่น ๆ เพื่อใช้สถานการณ์สมมติกรณีที่ดีที่สุดของการมีหลาย โปรเซสเซอร์ในเครื่อง

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

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

สรุป

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

ขอบคุณที่อ่านจนจบ เจอกันใหม่บทความหน้าค่ะ

อาร์นาบ มอนดัล

Python Developer & Data Engineer | นักเขียนเทคโนโลยีอิสระ

เชื่อมโยงไปยังบทความอื่น ๆ ของฉัน

ลิงก์ไปยังสมุดบันทึกการทำงานร่วมกัน

แหล่งรูปภาพ:

  1. ภาพที่ 1: https://unsplash.com/photos/BXOXnQ26B7o
  2. ภาพที่ 2: https://unsplash.com/photos/ePAL0f6jjr0

สื่อที่แสดงในบทความนี้ไม่ใช่ของ Analytics Vidhya และใช้ดุลยพินิจของผู้เขียน

ที่มา: https://www.analyticsvidhya.com/blog/2021/10/dynamic-time-warping-explained-using-python-and-har-dataset/

ประทับเวลา:

เพิ่มเติมจาก การวิเคราะห์ วิทยา