การคาดการณ์การชอบ: ภายในอัลกอริทึมของกลไกแนะนำอย่างง่าย

เผยแพร่แล้ว: 2022-03-11

เอ็นจิ้นการแนะนำ (บางครั้งเรียกว่าระบบผู้แนะนำ) เป็นเครื่องมือที่ช่วยให้นักพัฒนาอัลกอริธึมทำนายสิ่งที่ผู้ใช้อาจชอบหรือไม่ชอบในรายการที่กำหนด เครื่องมือแนะนำเป็นทางเลือกที่น่าสนใจสำหรับช่องค้นหา เนื่องจากเครื่องมือแนะนำช่วยให้ผู้ใช้ค้นพบผลิตภัณฑ์หรือเนื้อหาที่อาจไม่พบ ทำให้เครื่องมือแนะนำเป็นส่วนสำคัญของเว็บไซต์และบริการต่างๆ เช่น Facebook, YouTube, Amazon และอื่นๆ

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

การสร้างเครื่องมือแนะนำ

ในบทช่วยสอนนี้ เราจะแนะนำคุณตลอดขั้นตอนการสร้างเอ็นจิ้นการแนะนำที่ทำงานร่วมกันและอิงตามหน่วยความจำ เครื่องมือแนะนำนี้จะแนะนำภาพยนตร์ให้กับผู้ใช้โดยพิจารณาจากสิ่งที่พวกเขาชอบและไม่ชอบ และจะทำงานเหมือนตัวอย่างที่สองที่กล่าวถึงก่อนหน้านี้ สำหรับโครงการนี้ เราจะใช้การดำเนินการชุดพื้นฐาน คณิตศาสตร์เล็กน้อย และ Node.js/CoffeeScript สามารถดูซอร์สโค้ดทั้งหมดที่เกี่ยวข้องกับบทช่วยสอนนี้ได้ที่นี่

เซตและสมการ

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

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

ในการคำนวณดัชนีความคล้ายคลึงกัน เราจะใช้รูปแบบของสูตรดัชนี Jaccard เดิมเรียกว่า “coefficient de communaute” (ประกาศเกียรติคุณ Paul Jaccard) สูตรนี้เปรียบเทียบสองชุดและสร้างสถิติทศนิยมอย่างง่ายระหว่าง 0 ถึง 1.0:

ดัชนีความคล้ายคลึงกัน

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

สูตรดัชนีแจ็คการ์ด

ในที่นี้ U1 และ U2 คือผู้ใช้สองคนที่เรากำลังเปรียบเทียบ และ L1 และ L2 เป็นชุดของภาพยนตร์ที่ U1 และ U2 ชอบตามลำดับ ทีนี้ ถ้าคุณลองคิดดู ผู้ใช้สองคนที่ชอบดูหนังเรื่องเดียวกันก็คล้ายกัน แล้วผู้ใช้สองคนที่ไม่ชอบดูหนังเรื่องเดียวกันก็ควรจะคล้ายกันด้วย นี่คือที่ที่เราแก้ไขสมการเล็กน้อย:

แก้ไขสมการ

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

ดัชนีความคล้ายคลึงกันของผู้ใช้สองคน

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

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

อัลกอริธึมเครื่องยนต์แนะนำ

ลองแยกสมการนี้ออกเล็กน้อย สิ่งที่เราหมายถึงโดย P(U,M) คือความเป็นไปได้ที่ผู้ใช้ U จะชอบภาพยนตร์ M ZL และ ZD คือผลรวมของดัชนีความคล้ายคลึงของผู้ใช้ U กับผู้ใช้ทั้งหมดที่ชอบหรือไม่ชอบภาพยนตร์เรื่อง M ตามลำดับ |ML|+|MD| หมายถึงจำนวนผู้ใช้ทั้งหมดที่ชอบหรือไม่ชอบภาพยนตร์เรื่อง M ผลลัพธ์ P(U,M) สร้างตัวเลขระหว่าง -1.0 ถึง 1.0

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

การสร้างเครื่องมือแนะนำ

เราจะสร้างเอ็นจิ้นคำแนะนำนี้เป็นแอปพลิเคชัน Node.js ที่ง่ายมาก ส่วนหน้าจะมีงานน้อยมาก ส่วนใหญ่เป็นหน้า HTML และแบบฟอร์มบางส่วน (เราจะใช้ Bootstrap เพื่อทำให้หน้าดูเรียบร้อย) ทางฝั่งเซิร์ฟเวอร์ เราจะใช้ CoffeeScript แอปพลิเคชันจะมีเส้นทาง GET และ POST สองสามเส้นทาง แม้ว่าเราจะมีแนวคิดเกี่ยวกับผู้ใช้ในแอปพลิเคชัน แต่เราจะไม่มีกลไกการลงทะเบียน/เข้าสู่ระบบที่ซับซ้อนใดๆ เพื่อความคงอยู่ เราจะใช้แพ็คเกจ Bourne ที่พร้อมใช้งานผ่าน NPM ซึ่งทำให้แอปพลิเคชันสามารถจัดเก็บข้อมูลในไฟล์ JSON ธรรมดา และดำเนินการสืบค้นฐานข้อมูลพื้นฐานกับไฟล์เหล่านั้น เราจะใช้ Express.js เพื่อทำให้กระบวนการจัดการเส้นทางและตัวจัดการง่ายขึ้น

ณ จุดนี้ หากคุณยังใหม่ต่อการพัฒนา Node.js คุณอาจต้องการโคลนที่เก็บ GitHub เพื่อให้ง่ายต่อการปฏิบัติตามบทช่วยสอนนี้ เช่นเดียวกับโปรเจ็กต์ Node.js อื่นๆ เราจะเริ่มต้นด้วยการสร้างไฟล์ package.json และติดตั้งชุดของแพ็คเกจการพึ่งพาที่จำเป็นสำหรับโปรเจ็กต์นี้ หากคุณกำลังใช้ที่เก็บโคลน ไฟล์ package.json ควรจะอยู่ที่นั่นแล้ว จากตำแหน่งที่การติดตั้งการขึ้นต่อกันจะทำให้คุณต้องดำเนินการ “$ npm install” การดำเนินการนี้จะติดตั้งแพ็กเกจทั้งหมดที่อยู่ในไฟล์ package.json

แพ็คเกจ Node.js ที่เราต้องการสำหรับโครงการนี้คือ:

  • async
  • บอร์น
  • กาแฟสคริปต์
  • ด่วน
  • หยก
  • ขีดเส้นใต้

เราจะสร้างเอ็นจิ้นการแนะนำโดยแยกวิธีการที่เกี่ยวข้องทั้งหมดออกเป็นสี่คลาส CoffeeScript แยกกัน แต่ละคลาสจะจัดเก็บภายใต้ “lib/engine”: Engine, Rater, Similars และ Suggestions คลาส Engine จะรับผิดชอบในการจัดหา API อย่างง่ายสำหรับเอ็นจินการแนะนำ และจะรวมอีกสามคลาสเข้าด้วยกัน ผู้ประเมินจะรับผิดชอบในการติดตามการชอบและไม่ชอบ (เป็นสองอินสแตนซ์แยกกันของคลาส Rater) ความคล้ายคลึงและคำแนะนำจะรับผิดชอบในการกำหนดและติดตามผู้ใช้ที่คล้ายกันและรายการที่แนะนำสำหรับผู้ใช้ตามลำดับ

ติดตามการชอบและไม่ชอบ

มาเริ่มกันที่คลาส Raters ของเราก่อน นี่เป็นวิธีง่ายๆ:

 class Rater constructor: (@engine, @kind) -> add: (user, item, done) -> remove: (user, item, done) -> itemsByUser: (user, done) -> usersByItem: (item, done) ->

ตามที่ระบุไว้ก่อนหน้าในบทช่วยสอนนี้ เราจะมี Rater หนึ่งตัวอย่างสำหรับการถูกใจ และอีกตัวอย่างหนึ่งสำหรับการไม่ชอบ หากต้องการบันทึกว่าผู้ใช้ชอบรายการใดรายการหนึ่ง เราจะส่งต่อไปยัง “ผู้ประเมิน#เพิ่ม()” ในทำนองเดียวกัน ในการลบการให้คะแนน เราจะส่งต่อไปยัง “Rater#remove()”

เนื่องจากเราใช้ Bourne เป็นโซลูชันฐานข้อมูลแบบไม่ใช้เซิร์ฟเวอร์ เราจะจัดเก็บการให้คะแนนเหล่านี้ในไฟล์ชื่อ “./db-#{@kind}.json” โดยที่ประเภทคือ “ชอบ” หรือ “ไม่ชอบ” เราจะเปิดฐานข้อมูลภายในตัวสร้างของอินสแตนซ์ Rater:

 constructor: (@engine, @kind) -> @db = new Bourne "./db-#{@kind}.json"

วิธีนี้จะทำให้การเพิ่มเรกคอร์ดการให้คะแนนทำได้ง่ายเพียงแค่เรียกเมธอดฐานข้อมูลของบอร์นภายในเมธอด “Rater#add()” ของเรา:

 @db.insert user: user, item: item, (err) =>

และคล้ายกับการลบออก ("db.delete" แทนที่จะเป็น "db.insert") อย่างไรก็ตาม ก่อนที่เราจะเพิ่มหรือลบบางสิ่ง เราต้องแน่ใจว่าไม่มีสิ่งนั้นอยู่ในฐานข้อมูล ตามหลักการแล้ว ด้วยฐานข้อมูลจริง เราสามารถทำให้มันเป็นการดำเนินการเดียวได้ สำหรับบอร์น เราต้องทำการตรวจสอบด้วยตนเองก่อน และเมื่อการแทรกหรือการลบเสร็จสิ้น เราจำเป็นต้องตรวจสอบให้แน่ใจว่าเราคำนวณดัชนีความคล้ายคลึงกันสำหรับผู้ใช้รายนี้ใหม่ แล้วจึงสร้างชุดคำแนะนำใหม่ เมธอด “Rater#add()” และ “Rater#remove()” จะมีลักษณะดังนี้:

 add: (user, item, done) -> @db.find user: user, item: item, (err, res) => if res.length > 0 return done() @db.insert user: user, item: item, (err) => async.series [ (done) => @engine.similars.update user, done (done) => @engine.suggestions.update user, done ], done remove: (user, item, done) -> @db.delete user: user, item: item, (err) => async.series [ (done) => @engine.similars.update user, done (done) => @engine.suggestions.update user, done ], done

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

อีกสองวิธีคือ “Rater#itemsByUser()” และ “Rater#usersByItem()” ของคลาสนี้จะเกี่ยวข้องกับการทำสิ่งที่ชื่อบอกเป็นนัย - ค้นหารายการที่ให้คะแนนโดยผู้ใช้และผู้ใช้ที่ให้คะแนนรายการตามลำดับ ตัวอย่างเช่น เมื่อ Rater สร้างอินสแตนซ์ด้วย kind = “likes” , “Rater#itemsByUser()” จะค้นหารายการทั้งหมดที่ผู้ใช้ให้คะแนน

ค้นหาผู้ใช้ที่คล้ายกัน

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

การค้นหาผู้ใช้ที่คล้ายกัน

เช่นเดียวกับคลาสก่อนหน้า Rater เราจะใส่ทุกอย่างลงในฐานข้อมูล Bourne ชื่อ “./db-similars.json” ซึ่งเราจะเปิดในตัวสร้างของ Rater คลาสจะมีเมธอด “Similars#byUser()” ซึ่งช่วยให้เราค้นหาผู้ใช้ที่คล้ายกับผู้ใช้ที่ระบุผ่านการค้นหาฐานข้อมูลอย่างง่าย:

 @db.findOne user: user, (err, {others}) =>

อย่างไรก็ตาม วิธีที่สำคัญที่สุดของคลาสนี้คือ “Similars#update()” ซึ่งทำงานโดยนำผู้ใช้และคำนวณรายชื่อผู้ใช้อื่นที่คล้ายคลึงกัน และจัดเก็บรายการในฐานข้อมูลพร้อมกับดัชนีความคล้ายคลึงกัน เริ่มต้นด้วยการค้นหาความชอบและไม่ชอบของผู้ใช้:

 async.auto userLikes: (done) => @engine.likes.itemsByUser user, done userDislikes: (done) => @engine.dislikes.itemsByUser user, done , (err, {userLikes, userDislikes}) => items = _.flatten([userLikes, userDislikes])

นอกจากนี้เรายังพบผู้ใช้ทั้งหมดที่ให้คะแนนรายการเหล่านี้:

 async.map items, (item, done) => async.map [ @engine.likes @engine.dislikes ], (rater, done) => rater.usersByItem item, done , done , (err, others) =>

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

 async.map others, (other, done) => async.auto otherLikes: (done) => @engine.likes.itemsByUser other, done otherDislikes: (done) => @engine.dislikes.itemsByUser other, done , (err, {otherLikes, otherDislikes}) => done null, user: other similarity: (_.intersection(userLikes, otherLikes).length+_.intersection(userDislikes, otherDislikes).length-_.intersection(userLikes, otherDislikes).length-_.intersection(userDislikes, otherLikes).length) / _.union(userLikes, otherLikes, userDislikes, otherDislikes).length , (err, others) => @db.insert user: user others: others , done

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

กำลังสร้างคำแนะนำ

ชั้นเรียนต่อไปของเรา ข้อเสนอแนะ คือที่ที่การคาดคะเนทั้งหมดเกิดขึ้น เช่นเดียวกับคลาสที่คล้ายคลึงกัน เราอาศัยฐานข้อมูล Bourne อื่นที่ชื่อว่า “./db-suggestions.json” ซึ่งเปิดขึ้นภายในตัวสร้าง

การสร้างข้อเสนอแนะและข้อเสนอแนะ

คลาสจะมีเมธอด “Suggestions#forUser()” เพื่อค้นหาคำแนะนำจากการคำนวณสำหรับผู้ใช้ที่ระบุ:

 forUser: (user, done) -> @db.findOne user: user, (err, {suggestions}={suggestion: []}) -> done null, suggestions

วิธีการที่จะคำนวณผลลัพธ์เหล่านี้คือ “Suggestions#update()” วิธีนี้ เช่น “Similars#update()” จะใช้ผู้ใช้เป็นอาร์กิวเมนต์ วิธีการเริ่มต้นด้วยการแสดงรายชื่อผู้ใช้ทั้งหมดที่คล้ายกับผู้ใช้ที่ระบุ และรายการทั้งหมดที่ผู้ใช้กำหนดไม่ได้ให้คะแนน:

 @engine.similars.byUser user, (err, others) => async.auto likes: (done) => @engine.likes.itemsByUser user, done dislikes: (done) => @engine.dislikes.itemsByUser user, done items: (done) => async.map others, (other, done) => async.map [ @engine.likes @engine.dislikes ], (rater, done) => rater.itemsByUser other.user, done , done , done , (err, {likes, dislikes, items}) => items = _.difference _.unique(_.flatten items), likes, dislikes

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

 @db.delete user: user, (err) => async.map items, (item, done) => async.auto likers: (done) => @engine.likes.usersByItem item, done dislikers: (done) => @engine.dislikes.usersByItem item, done , (err, {likers, dislikers}) => numerator = 0 for other in _.without _.flatten([likers, dislikers]), user other = _.findWhere(others, user: other) if other? numerator += other.similarity done null, item: item weight: numerator / _.union(likers, dislikers).length , (err, suggestions) =>

เมื่อเสร็จแล้วเราจะบันทึกกลับไปที่ฐานข้อมูล:

 @db.insert user: user suggestions: suggestions , done

การเปิดเผย Library API

ภายในคลาส Engine เรารวมทุกอย่างไว้ในโครงสร้างที่เหมือน API เพื่อให้เข้าถึงได้ง่ายจากโลกภายนอก:

 class Engine constructor: -> @likes = new Rater @, 'likes' @dislikes = new Rater @, 'dislikes' @similars = new Similars @ @suggestions = new Suggestions @

เมื่อเรายกตัวอย่างวัตถุ Engine:

 e = new Engine

เราสามารถเพิ่มหรือลบการชอบและไม่ชอบได้อย่างง่ายดาย:

 e.likes.add user, item, (err) -> e.dislikes.add user, item, (err) ->

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

 e.similars.update user, (err) -> e.suggestions.update user, (err) ->

สุดท้ายนี้ สิ่งสำคัญคือต้องส่งออกคลาส Engine นี้ (และคลาสอื่นๆ ทั้งหมด) จากไฟล์ ".coffee" ที่เกี่ยวข้อง:

 module.exports = Engine

จากนั้น ส่งออก Engine จากแพ็คเกจโดยสร้างไฟล์ “index.coffee” ด้วยบรรทัดเดียว:

 module.exports = require './engine'

การสร้างอินเทอร์เฟซผู้ใช้

เพื่อให้สามารถใช้อัลกอริธึมเอ็นจินคำแนะนำในบทช่วยสอนนี้ เราต้องการให้อินเทอร์เฟซผู้ใช้ที่เรียบง่ายบนเว็บ ในการทำเช่นนั้น เราวางแอป Express ในไฟล์ “web.iced” ของเราและจัดการเส้นทางสองสามเส้นทาง:

 movies = require './data/movies.json' Engine = require './lib/engine' e = new Eengine app = express() app.set 'views', "#{__dirname}/views" app.set 'view engine', 'jade' app.route('/refresh') .post(({query}, res, next) -> async.series [ (done) => e.similars.update query.user, done (done) => e.suggestions.update query.user, done ], (err) => res.redirect "/?user=#{query.user}" ) app.route('/like') .post(({query}, res, next) -> if query.unset is 'yes' e.likes.remove query.user, query.movie, (err) => res.redirect "/?user=#{query.user}" else e.dislikes.remove query.user, query.movie, (err) => e.likes.add query.user, query.movie, (err) => if err? return next err res.redirect "/?user=#{query.user}" ) app.route('/dislike') .post(({query}, res, next) -> if query.unset is 'yes' e.dislikes.remove query.user, query.movie, (err) => res.redirect "/?user=#{query.user}" else e.likes.remove query.user, query.movie, (err) => e.dislikes.add query.user, query.movie, (err) => res.redirect "/?user=#{query.user}" ) app.route('/') .get(({query}, res, next) -> async.auto likes: (done) => e.likes.itemsByUser query.user, done dislikes: (done) => e.dislikes.itemsByUser query.user, done suggestions: (done) => e.suggestions.forUser query.user, (err, suggestions) => done null, _.map _.sortBy(suggestions, (suggestion) -> -suggestion.weight), (suggestion) => _.findWhere movies, id: suggestion.item , (err, {likes, dislikes, suggestions}) => res.render 'index', movies: movies user: query.user likes: likes dislikes: dislikes suggestions: suggestions[...4] )

ภายในแอพ เราจัดการสี่เส้นทาง เส้นทางดัชนี “/” คือตำแหน่งที่เราให้บริการ HTML ส่วนหน้าโดยการแสดงเทมเพลต Jade การสร้างเทมเพลตต้องมีรายชื่อภาพยนตร์ ชื่อผู้ใช้ปัจจุบัน การชอบและไม่ชอบของผู้ใช้ และข้อเสนอแนะสี่อันดับแรกสำหรับผู้ใช้ ซอร์สโค้ดของเทมเพลต Jade นั้นไม่มีอยู่ในบทความ แต่มีอยู่ในที่เก็บ GitHub

เส้นทาง "/like" และ "/dislike" คือที่ที่เรายอมรับคำขอ POST เพื่อบันทึกการชอบและไม่ชอบของผู้ใช้ ทั้งสองเส้นทางเพิ่มการให้คะแนนโดยลบการให้คะแนนที่ขัดแย้งออกก่อน ถ้าจำเป็น ตัวอย่างเช่น ผู้ใช้ชอบสิ่งที่พวกเขาไม่ชอบก่อนหน้านี้จะทำให้ตัวจัดการลบคะแนน "ไม่ชอบ" ออกก่อน เส้นทางเหล่านี้ยังอนุญาตให้ผู้ใช้ "ไม่ชอบ" หรือ "ไม่ชอบ" รายการหากต้องการ

สุดท้าย เส้นทาง “/รีเฟรช” อนุญาตให้ผู้ใช้สร้างชุดคำแนะนำใหม่ได้ตามต้องการ แม้ว่าการดำเนินการนี้จะดำเนินการโดยอัตโนมัติทุกครั้งที่ผู้ใช้ให้คะแนนรายการ

ทดลองขับ

หากคุณพยายามใช้แอปพลิเคชันนี้ตั้งแต่เริ่มต้นโดยทำตามบทความนี้ คุณจะต้องดำเนินการขั้นตอนสุดท้ายก่อนจึงจะทดสอบได้ คุณจะต้องสร้างไฟล์ “.json” ที่ “data/movies.json” และเติมข้อมูลด้วยข้อมูลภาพยนตร์ดังนี้:

 [ { "id": "1", "name": "Transformers: Age of Extinction", "thumb": { "url": "//upload.wikimedia.org/wikipedia/en/7/7f/Inception_ver3.jpg" } }, // … ]

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

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

 $ npm start

สมมติว่าทุกอย่างดำเนินไปอย่างราบรื่น คุณควรเห็นข้อความต่อไปนี้ปรากฏบนเทอร์มินัล:

 Listening on 5000

เนื่องจากเราไม่ได้ใช้ระบบตรวจสอบผู้ใช้จริงใดๆ แอปพลิเคชันต้นแบบจึงอาศัยเฉพาะชื่อผู้ใช้ที่เลือกหลังจากไปที่ “http://localhost:5000” เมื่อป้อนชื่อผู้ใช้และส่งแบบฟอร์มแล้ว คุณควรไปที่หน้าอื่นที่มีสองส่วน: “ภาพยนตร์แนะนำ” และ “ภาพยนตร์ทั้งหมด” เนื่องจากเราขาดองค์ประกอบที่สำคัญที่สุดของกลไกแนะนำ (ข้อมูล) ที่ใช้หน่วยความจำร่วมกัน เราจึงไม่สามารถแนะนำภาพยนตร์ใดๆ ให้กับผู้ใช้ใหม่รายนี้ได้

ณ จุดนี้ คุณควรเปิดหน้าต่างเบราว์เซอร์อื่นไปที่ “http://localhost:5000” และเข้าสู่ระบบในฐานะผู้ใช้อื่นที่นั่น ชอบและไม่ชอบหนังบางเรื่องในฐานะผู้ใช้คนที่สองนี้ กลับไปที่หน้าต่างเบราว์เซอร์ของผู้ใช้รายแรกและให้คะแนนภาพยนตร์บางเรื่องด้วย ตรวจสอบให้แน่ใจว่าคุณให้คะแนนภาพยนตร์ทั่วไปอย่างน้อยสองสามเรื่องสำหรับผู้ใช้ทั้งสอง คุณควรเริ่มเห็นคำแนะนำทันที

การปรับปรุง

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

การปรับปรุงที่ชัดเจนอย่างหนึ่งที่นี่คือการใช้ฐานข้อมูลจริง แทนโซลูชันแบบไฟล์ของเรา โซลูชันแบบอิงไฟล์อาจทำงานได้ดีในต้นแบบขนาดเล็ก แต่ก็ไม่ใช่ตัวเลือกที่สมเหตุสมผลสำหรับการใช้งานจริง ทางเลือกหนึ่งในหลายๆ อย่างคือ Redis Redis นั้นรวดเร็วและมีความสามารถพิเศษที่เป็นประโยชน์เมื่อต้องจัดการกับโครงสร้างข้อมูลแบบ set-like

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

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

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

บทสรุป

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

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