อัลกอริทึม HyperLogLog ทำงานอย่างไร


172

ฉันได้เรียนรู้เกี่ยวกับอัลกอริทึมที่แตกต่างกันในเวลาว่างของฉันเมื่อเร็ว ๆ นี้และสิ่งที่ฉันได้พบซึ่งน่าสนใจมากเรียกว่าอัลกอริทึม HyperLogLog - ซึ่งประมาณจำนวนรายการที่ไม่ซ้ำกันในรายการ

นี่เป็นเรื่องที่น่าสนใจเป็นพิเศษสำหรับฉันเพราะมันทำให้ฉันย้อนกลับไปยังยุค MySQL ของฉันเมื่อฉันเห็นว่า "ความสำคัญ" (ซึ่งฉันคิดเสมอจนกระทั่งเมื่อไม่นานมานี้ว่ามันไม่ได้ถูกคำนวณ)

ดังนั้นฉันจึงรู้วิธีเขียนอัลกอริธึมในO ( n ) ที่จะคำนวณจำนวนเฉพาะของไอเท็มในอาเรย์ ฉันเขียนสิ่งนี้ใน JavaScript:

function countUniqueAlgo1(arr) {
    var Table = {};
    var numUnique = 0;
    var numDataPoints = arr.length;
    for (var j = 0; j < numDataPoints; j++) {
        var val = arr[j];
        if (Table[val] != null) {
            continue;
        }
        Table[val] = 1;
        numUnique++;
    }
    return numUnique;
}

แต่ปัญหาคืออัลกอริทึมของฉันในขณะที่O ( n ) ใช้หน่วยความจำจำนวนมาก (เก็บค่าไว้Table)

ฉันได้อ่านบทความนี้เกี่ยวกับวิธีนับจำนวนซ้ำในรายการในเวลาO ( n ) และใช้หน่วยความจำน้อยที่สุด

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

ฉันอ่านกระดาษ แต่ฉันไม่สามารถเข้าใจได้ ใครสามารถให้คำอธิบายเพิ่มเติมจากคนธรรมดาได้บ้าง? ฉันรู้ว่าแฮชคืออะไร แต่ฉันไม่เข้าใจว่าจะใช้งานอย่างไรในอัลกอริทึม HyperLogLog นี้


4
บทความนี้ ( research.google.com/pubs/pub40671.html ) ยังสรุปอัลกอริทึม HyperLogLog และการปรับปรุงบางอย่าง ฉันคิดว่ามันง่ายกว่าที่จะเข้าใจมากกว่าเอกสารต้นฉบับ
zhanxw

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

คำตอบ:


153

กลอุบายหลักที่อยู่เบื้องหลังอัลกอริธึมนี้คือถ้าคุณสังเกตการไหลของจำนวนเต็มแบบสุ่มดูจำนวนเต็มซึ่งการแสดงแบบไบนารี่เริ่มต้นด้วยคำนำหน้าบางรู้จักมีโอกาสสูงกว่าที่ความสำคัญของกระแสคือ 2 ^ (ขนาดของคำนำหน้า) .

นั่นคือในจำนวนกระแสสุ่มของจำนวนเต็ม ~ 50% ของตัวเลข (ในไบนารี) เริ่มต้นด้วย "1", 25% เริ่มต้นด้วย "01", 12,5% เริ่มต้นด้วย "001" ซึ่งหมายความว่าหากคุณสังเกตการสตรีมแบบสุ่มและดู "001" จะมีโอกาสสูงกว่าที่สตรีมนี้จะมีความสำคัญเท่ากับ 8

(คำนำหน้า "00..1" ไม่มีความหมายพิเศษมันมีเพราะมันง่ายที่จะหาบิตที่สำคัญที่สุดในจำนวนเลขฐานสองในโปรเซสเซอร์ส่วนใหญ่)

แน่นอนถ้าคุณสังเกตเพียงจำนวนเต็มโอกาสที่ค่านี้ผิดจะสูง นั่นเป็นสาเหตุที่อัลกอริทึมแบ่งกระแสใน "sub" สตรีมอิสระและรักษาความยาวสูงสุดของคำนำหน้า "00 ... 1" ของแต่ละสตรีม จากนั้นประเมินค่าสุดท้ายโดยรับค่าเฉลี่ยของแต่ละสตรีมย่อย

นั่นเป็นแนวคิดหลักของอัลกอริทึมนี้ มีรายละเอียดบางส่วนที่ขาดหายไป (ตัวอย่างเช่นการแก้ไขค่าประมาณต่ำ) แต่ทั้งหมดเขียนได้ดีในกระดาษ ขออภัยภาษาอังกฤษแย่มาก


"มีโอกาสสูงกว่าที่สตรีมนี้มีความสำคัญเป็น 8" คุณช่วยอธิบายหน่อยได้ไหมว่าเพราะเหตุใด 000 จึงหมายถึงจำนวนการทดลองที่คาดหมาย 2 ^ 3 ฉันพยายามคำนวณความคาดหวังทางคณิตศาสตร์ของจำนวนการทดลองสมมติว่าเรามีการวิ่งอย่างน้อยหนึ่งครั้งด้วย 3 ศูนย์และไม่มีการวิ่งด้วยศูนย์ 4 ตัว ...
yura

5
ไม่ค่อยเข้าใจกระดาษจนกว่าฉันจะอ่าน ตอนนี้มันสมเหตุสมผลแล้ว
josiah

5
@ yura ฉันรู้ว่ามันเป็นความคิดเห็นที่เก่าแก่มาก แต่มันอาจจะมีประโยชน์สำหรับคนอื่น ๆ เขากล่าวว่า "นั่นคือในจำนวนเต็มของจำนวนเต็ม (... ) 12,5% เริ่มต้นด้วย" 001 " ความน่าจะเป็นที่จะเกิดขึ้นคือ 8 เพราะ 12,5% หมายถึงหนึ่งในแปดของสตรีมทั้งหมด
braunmagrin

111

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

ก่อนที่จะดูว่าอัลกอริทึม HyperLogLog ทำสิ่งนี้ได้อย่างไรคุณต้องเข้าใจว่าทำไมคุณจึงต้องการมัน ปัญหาเกี่ยวกับวิธีที่ตรงไปตรงมาก็คือการใช้O(distinct elements)พื้นที่ ทำไมถึงมีสัญกรณ์ O ขนาดใหญ่ที่นี่แทนที่จะเป็นเพียงแค่องค์ประกอบที่แตกต่างกัน? นี่เป็นเพราะองค์ประกอบอาจมีขนาดแตกต่างกัน องค์ประกอบหนึ่งสามารถเป็นองค์ประกอบอื่น1 "is this big string"ดังนั้นถ้าคุณมีรายการจำนวนมาก (หรือองค์ประกอบจำนวนมาก) มันจะใช้หน่วยความจำมาก


การนับความน่าจะเป็น

เราจะได้ประมาณจำนวนองค์ประกอบที่เป็นเอกลักษณ์ได้อย่างไร สมมติว่าคุณมีสตริงmที่{0, 1}มีความน่าจะเป็นเท่ากัน ความน่าจะเป็นที่จะเริ่มต้นด้วย 0 ด้วย 2 ศูนย์ด้วย k เป็นเท่าไหร่? มันเป็น1/2, และ1/4 1/2^kซึ่งหมายความว่าหากคุณพบสตริงที่มีkเลขศูนย์คุณจะต้องตรวจสอบ2^kองค์ประกอบต่างๆโดยประมาณ ดังนั้นนี่คือจุดเริ่มต้นที่ดี มีรายการองค์ประกอบที่มีการกระจายอย่างเท่าเทียมกันระหว่าง0และ2^k - 1คุณสามารถนับจำนวนสูงสุดของคำนำหน้าที่ใหญ่ที่สุดของศูนย์ในการเป็นตัวแทนไบนารีและสิ่งนี้จะให้ประมาณการที่สมเหตุสมผล

ปัญหาคือการสันนิษฐานว่าการมีตัวเลขที่กระจายอย่างสม่ำเสมอจาก0t 2^k-1นั้นยากเกินกว่าจะทำได้ (ข้อมูลที่เราพบส่วนใหญ่ไม่ใช่ตัวเลขแทบจะไม่กระจายเท่า ๆ กันและสามารถอยู่ระหว่างค่าใดก็ได้ แต่การใช้ฟังก์ชัน hashing ที่ดีคุณสามารถสันนิษฐานได้ว่า เอาท์พุทบิตจะถูกกระจายอย่างเท่าเทียมกันและฟังก์ชั่นการแฮ็กส่วนใหญ่มีเอาต์พุตระหว่าง0และ2^k - 1( SHA1ให้ค่าระหว่าง0และ2^160) ดังนั้นสิ่งที่เราประสบความสำเร็จจนถึงตอนนี้คือเราสามารถประมาณจำนวนองค์ประกอบที่ไม่ซ้ำกันด้วยความสำคัญสูงสุดของkบิตlog(k)บิตขนาดหนึ่งจำนวนข้อเสียคือเรามีความแปรปรวนอย่างมากในการประเมินของเราสิ่งดีๆที่เราเกือบสร้างขึ้นกระดาษนับความน่าจะเป็นของปี 1984 (มันค่อนข้างฉลาดกว่าที่คาดไว้ แต่ก็ยังใกล้กัน)

LogLog

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

พวกเขาใช้แฮชหนึ่งอัน แต่แบ่งออกเป็นสองส่วน หนึ่งเรียกว่าฝากข้อมูล (จำนวนถังทั้งหมด2^x) และอีกอัน - โดยทั่วไปเหมือนกับแฮชของเรา มันยากสำหรับฉันที่จะได้รับสิ่งที่เกิดขึ้นดังนั้นฉันจะยกตัวอย่าง สมมติคุณมีสององค์ประกอบและฟังก์ชันแฮชของคุณซึ่งจะช่วยให้รูปแบบค่า0การ2^10ผลิต 2 ค่า: และ344 387คุณตัดสินใจที่จะมี 16 ถัง ดังนั้นคุณมี:

0101 011000  bucket 5 will store 1
0110 000011  bucket 6 will store 4

การมีที่เก็บมากขึ้นทำให้คุณลดความแปรปรวน (คุณใช้พื้นที่เพิ่มขึ้นเล็กน้อย แต่ก็ยังเล็กอยู่) การใช้ทักษะทางคณิตศาสตร์พวกเขาสามารถหาจำนวนข้อผิดพลาด (ซึ่งก็คือ1.3/sqrt(number of buckets))

HyperLogLog

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


และฉันต้องการจบด้วยกระดาษล่าสุดซึ่งแสดงอัลกอริทึม hyperLogLog รุ่นปรับปรุง (จนถึงตอนนี้ฉันยังไม่มีเวลาที่จะเข้าใจอย่างถ่องแท้ แต่ภายหลังฉันจะปรับปรุงคำตอบนี้)


2
ฉันคิดว่าในทางทฤษฎีแล้วk zeroesมันไม่ได้เป็นสิ่งที่พิเศษ คุณสามารถค้นหาแทนk onesและตรรกะจะเหมือนกันหรือแม้กระทั่งมองหาk lengthสตริงของ{0,1}แต่ใช้หนึ่งสายดังกล่าวและติดกับมันได้หรือไม่ เพราะทุกคนมีความน่าจะเป็นเท่ากับ 1/2 ^ k ในกรณีของสตริงไบนารี่
user881300

3
HyperLogLog จะไม่ลบ 30% ของจำนวนที่มากที่สุด นี่เป็นแนวคิดของอัลกอริทึม SuperLogLog ที่อธิบายไว้ในกระดาษ LogLog แนวคิดหลักของอัลกอริทึม HyperLogLog คือการหาค่าเฉลี่ยของกำลังสองโดยใช้ค่าเฉลี่ยฮาร์มอนิกแทนค่าเฉลี่ยเรขาคณิตที่ใช้โดย SuperLogLog และ LogLog
otmar

21

สัญชาตญาณคือถ้าการป้อนข้อมูลของคุณเป็นชุดสุ่มจำนวนมาก (เช่นค่าแฮช) พวกเขาควรกระจายอย่างสม่ำเสมอในช่วง สมมติว่าช่วงมีค่าสูงสุด 10 บิตเพื่อแทนค่าสูงสุด 1024 แล้วสังเกตค่าต่ำสุด สมมุติว่ามันคือ 10 จากนั้นค่าคาดคะเนของ cardinality จะอยู่ที่ประมาณ 100 (10 × 100 ≈ 1024)

อ่านบทความสำหรับตรรกะที่แท้จริงของหลักสูตร

อีกคำอธิบายที่ดีพร้อมรหัสตัวอย่างสามารถดูได้ที่นี่:
Damn Cool Algorithms: Cardinality Estimation - Nick's Blog


3
upvoted สำหรับลิงก์ไปยังบล็อกโพสต์อัลกอริทึมเจ๋ง ที่ช่วยให้ฉันเข้าใจอัลกอริทึมจริงๆ
Igor Serebryany
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.