πŸ€– ДСдубликация: ΠΊΠ°ΠΊ OpenAI ΠΈ FastAPI спасут Habr ΠΎΡ‚ Π΄ΡƒΠ±Π»Π΅ΠΈΜ†

Π’ ΡΡ‚Π°Ρ‚ΡŒΠ΅ рассказываСтся ΠΎ Ρ‚ΠΎΠΌ, ΠΊΠ°ΠΊ ΠΌΠΎΠ΄Π΅Π»ΠΈ OpenAI ΠΏΠΎΠΌΠΎΠ³Π°ΡŽΡ‚ Π² Π·Π°Π΄Π°Ρ‡Π΅ Π΄Π΅Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ тСкстов ΠΈ similarity search. РассмотрСны Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Π΅ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Ρ‹ ΠΊ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡŽ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹: ΠΎΡ‚ ΠΊΠΎΠ½Ρ†Π΅ΠΏΡ†ΠΈΠΈ MinHash Π΄ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Π½Π° эмбСддингах соврСмСнных трансформСнных ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ. Π’ ΡΡ‚Π°Ρ‚ΡŒΠ΅ Ρ‚Π°ΠΊΠΆΠ΅ описан ΠΏΡ€ΠΈΠΌΠ΅Ρ€ создания микросСрвиса Π½Π° FastAPI для поиска Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚ΠΎΠ² постов.

πŸ€– ДСдубликация: ΠΊΠ°ΠΊ OpenAI ΠΈ FastAPI спасут Habr ΠΎΡ‚ Π΄ΡƒΠ±Π»Π΅ΠΈΜ†

OpenAI ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅Ρ‚ Π½Π°Π²ΠΎΠ΄ΠΈΡ‚ΡŒ суСту. ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ ChatGPT ΠΏΠ΅Ρ€Π΅Π²Π°Π»ΠΈΠ»ΠΎ Π·Π° 100 ΠΌΠΈΠ»Π»ΠΈΠΎΠ½ΠΎΠ², Π½ΠΎ Ρ…Π°ΠΉΠΏ Π΄Π°ΠΆΠ΅ ΠΈ Π½Π΅ Π΄ΡƒΠΌΠ°Π΅Ρ‚ ΡΡ‚ΠΈΡ…Π°Ρ‚ΡŒ – каТСтся, Π΄Π°ΠΆΠ΅ Π±Π°Π±ΡƒΡˆΠΊΠΈ Ρƒ подъСзда ΡˆΠ»ΡŽΡ‚ ИИ свои вопросы ΠΏΡ€ΠΎ ΠΏΠΎΠ²Ρ‹ΡˆΠ΅Π½ΠΈΠ΅ пСнсии. Но сСгодня ΠΏΠΎΠΉΠ΄Π΅Ρ‚ Ρ€Π΅Ρ‡ΡŒ Π½Π΅ ΠΎ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½Ρ‹Ρ… вопросах, Π° ΠΎ Ρ‚ΠΎΠΌ, ΠΊΠ°ΠΊ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΌΠΎΠ΄Π΅Π»ΠΈ OpenAI для Π·Π°Π΄Π°Ρ‡ΠΈ Π΄Π΅Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ.

ДСдубликация (Deduplication), similarity search ΡΠ²Π»ΡΡŽΡ‚ΡΡ вострСбованными Π΄ΠΎΠΌΠ΅Π½Π°ΠΌΠΈ машинного обучСния. Π’ ΠΊΠΎΠΌΠ°Π½Π΄Π΅ новостного ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³Π° Π‘Π±Π΅Ρ€Π° я Ρ€Π΅ΡˆΠ°Π» эти Π·Π°Π΄Π°Ρ‡ΠΈ для ΠΏΠΎΡ‚ΠΎΠΊΠ° новостСй. БСйчас я Ρ€Π°Π±ΠΎΡ‚Π°ΡŽ Π² ΠΊΠΎΠΌΠ°Π½Π΄Π΅ Data Science Π‘Π°ΠΌΠΎΠ»Π΅Ρ‚Π° (это Ρ‚Π°ΠΊΠΎΠΉ застройщик Π·Π΄ΠΎΡ€ΠΎΠ²ΠΎΠ³ΠΎ Ρ‡Π΅Π»ΠΎΠ²Π΅ΠΊΠ°) ΠΈ оказалось, Ρ‡Ρ‚ΠΎ здСсь Ρ‚ΠΎΡ‡Π΅ΠΊ прилоТСния для NLP Π΄Π°ΠΆΠ΅ большС Ρ‡Π΅ΠΌ Π² Π±Π°Π½ΠΊΠ΅, Π² Ρ‚ΠΎΠΌ числС ΠΈ для мэтчинга.

Π’ этой ΡΡ‚Π°Ρ‚ΡŒΠ΅ я ΠΏΠΎΡΡ‚Π°Ρ€Π°ΡŽΡΡŒ Ρ€Π°ΡΡΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΎ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π°Ρ… ΠΊ Π΄Π΅Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ тСкстов, ΠΊΠ°ΠΊ Π² этом ΠΏΠΎΠΌΠΎΠ³Π°ΡŽΡ‚ ΠΌΠΎΠ΄Π΅Π»ΠΈ OpenAI, Π° Ρ‚Π°ΠΊΠΆΠ΅ ΠΏΡ€ΠΈΠ²Π΅Π΄Ρƒ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π±ΠΎΠ΅Π²ΠΎΠ³ΠΎ микросСрвиса Π½Π° FastAPI, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ Π½Π°Ρ…ΠΎΠ΄ΠΈΡ‚ΡŒ Π½Π΅ ΠΎΡ‡Π΅Π½ΡŒ свСТиС ΠΏΠΎ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Π½ΠΈΡŽ посты Π½Π° Π₯Π°Π±Ρ€Π΅. МнС ΠΈΠΌΠΏΠΎΠ½ΠΈΡ€ΡƒΠ΅Ρ‚ историчСский ΠΏΠΎΠ΄Ρ…ΠΎΠ΄, поэтому я сначала расскаТу ΠΏΡ€ΠΎ Ρ‚Π΅ΠΌΠ½ΠΎΠ΅ ΠΏΡ€ΠΎΡˆΠ»ΠΎΠ΅, ΠΈ постСпСнно добСрСмся Π΄ΠΎ Π·ΠΎΠ»ΠΎΡ‚ΠΎΠ³ΠΎ Π²Π΅ΠΊΠ° соврСмСнных языковых ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ.

Π‘Ρ‚Π°Ρ€ΠΎΠ΅, Π΄ΠΎΠ±Ρ€ΠΎΠ΅, Π²Π΅Ρ‡Π½ΠΎΠ΅

Π”Π°Π²Π°ΠΉΡ‚Π΅ сначала разбСрСмся, ΠΊΠ°ΠΊ Π²ΠΎΠΎΠ±Ρ‰Π΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΎΡ†Π΅Π½ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ Π΄Π²Π° тСкста ΡΠ²Π»ΡΡŽΡ‚ΡΡ ΠΏΠΎΡ…ΠΎΠΆΠΈΠΌΠΈ. Ну, самоС ΠΎΡ‡Π΅Π²ΠΈΠ΄Π½ΠΎΠ΅ – ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° ΠΎΠ±Ρ‰ΠΈΠ΅ слова, Ρ‡Π΅ΠΌ ΠΈΡ… большС, Ρ‚Π΅ΠΌ Π±Π»ΠΈΠΆΠ΅ Π΄Π²Π° тСкста. БобствСнно эту идСю ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ ΠΌΠ΅Ρ€Π° Π–Π°ΠΊΠΊΠ°Ρ€Π°.

ΠœΠ΅Ρ€Π° Π–Π°ΠΊΠΊΠ°Ρ€Π°
ΠœΠ΅Ρ€Π° Π–Π°ΠΊΠΊΠ°Ρ€Π°

Однако Π² Ρ‚Π°ΠΊΠΎΠΌ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π΅ Π΅ΡΡ‚ΡŒ свои минусы. Π’ΠΎ-ΠΏΠ΅Ρ€Π²Ρ‹Ρ…, ΠΎΠ½ Π½Π΅ ΠΎΠΏΡ‚ΠΈΠΌΠ°Π»Π΅Π½ с Π²Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния. Если Ρƒ вас большой корпус Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ², Π° Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Ρ‹ состоят ΠΈΠ· тысяч слов, Ρ‚ΠΎ квадратичная ΡΠ»ΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΡ…ΠΎΡ€ΠΎΠ½ΠΈΡ‚ΡŒ ваши рСсурсы. Π’ΠΎ-Π²Ρ‚ΠΎΡ€Ρ‹Ρ…, отсутствиС ΠΎΠ±Ρ‰ΠΈΡ… слов Π΅Ρ‰Π΅ Π½Π΅ ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚ смыслового отличия.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Π½Π° Π½Π΅ ΠΎΠΏΡ‚ΠΈΠΌΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΈ Π–Π°ΠΊΠΊΠ°Ρ€Π°
doc А = 'Π¦Π΅Π½Π° Π½Π΅Ρ„Ρ‚ΠΈ Π±Ρ€Π΅Π½Π΄ выросла ΠΈΠ·-Π·Π° опасСний возмоТности столкновСния ΠΌΠ΅ΠΆΠ΄Ρƒ БША ΠΈ Π˜Ρ€Π°Π½ΠΎΠΌ'

doc B = 'Π‘Ρ‚ΠΎΠΈΠΌΠΎΡΡ‚ΡŒ сСвСроморской Π½Π΅Ρ„Ρ‚ΠΈ растСт Π½Π° ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΠΈ обострСния ΠΊΠΎΠ½Ρ„Π»ΠΈΠΊΡ‚Π° ΠŸΠ΅Π½Ρ‚Π°Π³ΠΎΠ½Π° ΠΈ Π˜Ρ€Π°Π½Π°β€™

Π’ Π΄Π°Π½Π½ΠΎΠΌ случаС ΠΌΠ΅Ρ€Π° Π–Π°ΠΊΠΊΠ°Ρ€Π° Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΠΊΠΎΠ»ΠΎ 0.1

ΠžΠ±ΠΎΠΉΡ‚ΠΈ ΠΏΠ΅Ρ€Π²ΡƒΡŽ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ‚ связка minhash + lsh. Minhash-ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Π² случайной пСрСстановкС слов (Π½Π° самом Π΄Π΅Π»Π΅ кусочков слов – шинглов) ΠΈ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠΈ. Π’ΠΎ Π΅ΡΡ‚ΡŒ тСкст любой Π΄Π»ΠΈΠ½Ρ‹ прСвращаСтся Π² Π½Π°Π±ΠΎΡ€ Ρ‚Π°ΠΊΠΈΡ… Ρ…Π΅ΡˆΠ΅ΠΉ (сигнатура): Π±Ρ‹Π»ΠΎ 10 тыс. слов, Π° Π»Π΅Π³ΠΊΠΈΠΌ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ΠΌ Ρ€ΡƒΠΊΠΈ Ρ…Π΅Ρˆ-Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ всС прСвратится Π² сигнатуру Π΄Π»ΠΈΠ½Ρ‹ 200. Ну Π° дальшС Π²Ρ‹ ΡƒΠΆΠ΅ сравниваСтС Ρ‚Π°ΠΊΠΈΠ΅ сигнатуры. Π­Ρ‚ΠΎ ΠΏΠΎΠΊΠ° Π½Π΅ избавляСт ΠΎΡ‚ ΠΊΠ²Π°Π΄Ρ€Π°Ρ‚ΠΈΡ‡Π½ΠΎΠΉ слоТности, Π½ΠΎ Π·Π°Ρ‚ΠΎ ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ‚ быстрСС ΠΏΡ€ΠΎΡΡ‡ΠΈΡ‚Ρ‹Π²Π°Ρ‚ΡŒ Π±Π»ΠΈΠ·ΠΎΡΡ‚ΡŒ. А Π²ΠΎΡ‚ LSH (local sensitive hashing) ΠΊΠ°ΠΊ Ρ€Π°Π· ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ‚ ΠΈΡΠΊΠ°Ρ‚ΡŒ Π΄ΡƒΠ±Π»ΠΈ Π½Π΅ ΠΏΠΎ всСму корпусу, Π° ΠΏΠΎ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π½ΠΎΠΌΡƒ подмноТСству. Π’ ΠΏΠΈΡ‚ΠΎΠ½Π΅ Π΅ΡΡ‚ΡŒ ΡƒΠΆΠ΅ готовая рСализация этого ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Π² Π²ΠΈΠ΄Π΅ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ datasketch. Π§Ρ‚ΠΎ касаСтся Π²Ρ‚ΠΎΡ€ΠΎΠΉ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹, то… придСтся Ρ‡ΠΈΡ‚Π°Ρ‚ΡŒ дальшС.

Π‘ΠΎΠ»ΡŒΡˆΠ΅ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹Ρ… ΠΌΠ°Ρ‚Π΅Ρ€ΠΈΠ°Π»ΠΎΠ² Π²Ρ‹ Π½Π°ΠΉΠ΄Π΅Ρ‚Π΅ Π½Π° нашСм Ρ‚Π΅Π»Π΅Π³Ρ€Π°ΠΌ-ΠΊΠ°Π½Π°Π»Π΅ Β«Π‘ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° data scientist’а»

РасскаТи ΠΌΠ½Π΅ ΠΏΡ€ΠΎ эмбСддинги

Π­ΠΌΠ±Π΅Π΄Π΄ΠΈΠ½Π³ являСтся ΠΊΡ€Π°Π΅ΡƒΠ³ΠΎΠ»ΡŒΠ½Ρ‹ΠΌ ΠΊΠ°ΠΌΠ½Π΅ΠΌ всСго NLP, Π΄Π°, прямо ΠΊΠ°ΠΊ Ρ‡Π΅Ρ‚Π²Π΅Ρ€Ρ‚ΡŒΡ„ΡƒΠ½Ρ‚ΠΎΠ²Ρ‹ΠΉ Ρ‡ΠΈΠ·Π±ΡƒΡ€Π³Π΅Ρ€ Π² ΠΏΠΈΡ‚Π°Π½ΠΈΠΈ. Π­ΠΌΠ±Π΅Π΄Π΄ΠΈΠ½Π³ – это Π²Π΅ΠΊΡ‚ΠΎΡ€Π½ΠΎΠ΅ прСдставлСниС ΠΊΠ°ΠΊΠΎΠΉ-Ρ‚ΠΎ сущности: слова, прСдлоТСния, Π°Π±Π·Π°Ρ†Π° ΠΈ Π΄Π°ΠΆΠ΅ всСго тСкста. На самом Π΄Π΅Π»Π΅ Π²Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΌΠΎΠΆΠ½ΠΎ всС Ρ‡Ρ‚ΠΎ ΡƒΠ³ΠΎΠ΄Π½ΠΎ. Π‘ΠΈΠ³Π½Π°Ρ‚ΡƒΡ€Π° Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π° ΠΈΠ· тСкста Π²Ρ‹ΡˆΠ΅ Ρ‚ΠΎΠΆΠ΅, ΠΏΠΎ сути, являСтся Π²Π΅ΠΊΡ‚ΠΎΡ€ΠΎΠΌ, Π½ΠΎ эмбСддингом Π»ΡƒΡ‡ΡˆΠ΅ это Π½Π΅ Π½Π°Π·Ρ‹Π²Π°Ρ‚ΡŒ, Ρ‚. ΠΊ. эмбСддинг это Β«Ρ…ΠΎΡ€ΠΎΡˆΠΈΠΉΒ» Π²Π΅ΠΊΡ‚ΠΎΡ€, для ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ (сумма, Ρ€Π°Π·Π½ΠΎΡΡ‚ΡŒ, скалярноС ΠΏΡ€ΠΎΠΈΠ·Π²Π΅Π΄Π΅Π½ΠΈΠ΅) ΡΠ²Π»ΡΡŽΡ‚ΡΡ осмыслСнными. ΠšΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ° Π½ΠΈΠΆΠ΅ ΡƒΠΆΠ΅ Π½Π°Π±ΠΈΠ»Π° оскомину, Π½ΠΎ ΠΎΠ½Π° Ρ…ΠΎΡ€ΠΎΡˆΠΎ ΠΈΠ»Π»ΡŽΡΡ‚Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ Π½Π°Π΄ эмбСддингами. ΠšΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ° ΠΈΠ»Π»ΡŽΡΡ‚Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ классичСский word2vec ΠΈΠ· Π΄Π°Π»Π΅ΠΊΠΎΠ³ΠΎ 2013 Π³ΠΎΠ΄Π°.

word2vec

Π’Π΅ΠΊΡƒΡ‰ΠΈΠ΅ Π²Π΅ΠΊΡ‚ΠΎΡ€Π½Ρ‹Π΅ прСдставлСния ΡƒΠΆΠ΅ Π½Π°ΡΡ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΊΡ€ΡƒΡ‚Ρ‹, Ρ‡Ρ‚ΠΎ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΌΠΎΠΆΠ½ΠΎ ΠΈΠ· Π²Π΅ΠΊΡ‚ΠΎΡ€Π° Ρ„ΡƒΡ‚Π±ΠΎΠ»ΠΊΠΈ с ΠΊΠΎΡ€ΠΎΡ‚ΠΊΠΈΠΌΠΈ Ρ€ΡƒΠΊΠ°Π²Π°ΠΌΠΈ Π²Ρ‹Ρ‡Π΅ΡΡ‚ΡŒ Π²Π΅ΠΊΡ‚ΠΎΡ€ ΠΊΠΎΡ€ΠΎΡ‚ΠΊΠΎΠ³ΠΎ Ρ€ΡƒΠΊΠ°Π²Π°, ΠΏΠΎΡ‚ΠΎΠΌ ΠΏΡ€ΠΈΠ±Π°Π²ΠΈΡ‚ΡŒ Π²Π΅ΠΊΡ‚ΠΎΡ€ Π΄Π»ΠΈΠ½Π½ΠΎΠ³ΠΎ Ρ€ΡƒΠΊΠ°Π²Π° ΠΈ Π½Π°ΠΊΠΎΠ½Π΅Ρ† – ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΡƒ Ρ„ΡƒΡ‚Π±ΠΎΠ»ΠΊΠΈ с Π΄Π»ΠΈΠ½Π½Ρ‹ΠΌΠΈ Ρ€ΡƒΠΊΠ°Π²Π°ΠΌΠΈ.

Π’ word2vec Π²Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚Π΅ Π²Π΅ΠΊΡ‚ΠΎΡ€Π° для ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Ρ… слов, ΠΈ ΠΎΠ½ΠΈ фиксированныС. Π’ прСдлоТСниях Β«ΠšΠ»ΡŽΡ‡ для Π·Π°ΠΌΠΊΠ°Β» ΠΈ Β«Π­Ρ‚ΠΈ ΠΌΠ΅Ρ€Π·Π°Π²Ρ†Ρ‹ лишили мСня Ρ€ΠΎΠ΄ΠΎΠ²ΠΎΠ³ΠΎ Π·Π°ΠΌΠΊΠ°Β», слово Β«Π·Π°ΠΌΠΎΠΊΒ» Π±ΡƒΠ΄Π΅Ρ‚ ΠΈΠΌΠ΅Ρ‚ΡŒ ΠΎΠ΄Π½ΠΎ ΠΈ Ρ‚ΠΎ ΠΆΠ΅ Π²Π΅ΠΊΡ‚ΠΎΡ€Π½ΠΎΠ΅ прСдставлСниС.

Для сравнСния тСкстов ΠΈΠ· сотСн ΠΈ тысяч слов Π²Π°ΠΌ придСтся ΠΏΡ€ΠΈΠ΄ΡƒΠΌΠ°Ρ‚ΡŒ способ объСдинСния Π²Π΅ΠΊΡ‚ΠΎΡ€ΠΎΠ² Π² ΠΎΠ΄ΠΈΠ½. МоТно, ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎ, просто ΡƒΡΡ€Π΅Π΄Π½ΠΈΡ‚ΡŒ, Π½ΠΎ Ρ‚Π°ΠΊ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡ‚Π΅Ρ€ΡΡ‚ΡŒ ΠΎΡ‡Π΅Π½ΡŒ ΠΌΠ½ΠΎΠ³ΠΎ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ. Π”Ρ€ΡƒΠ³ΠΎΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ – это ΡΠΊΠ»Π°Π΄Ρ‹Π²Π°Ρ‚ΡŒ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Π΅ Π²Π΅ΠΊΡ‚ΠΎΡ€Π° с вСсами, Π° вСса Π²Π·ΡΡ‚ΡŒ ΠΈΠ· ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Ρ‹ TF-IDF.

Π­ΠΌΠ±Π΅Π΄Π΄ΠΈΠ½Π³ΠΈ Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ²

Когда Ρƒ вас Π΅ΡΡ‚ΡŒ Π²Π΅ΠΊΡ‚ΠΎΡ€Π° для Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ², Ρ‚ΠΎ Π½Π°ΠΉΡ‚ΠΈ ΠΈΡ… ΠΏΠΎΡ…ΠΎΠΆΠ΅ΡΡ‚ΡŒ ΠΌΠΎΠΆΠ½ΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ косинусной близости. Когда Π² 8 классС Π²Ρ‹ Ρ€Π΅ΡˆΠ°Π»ΠΈ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ Π½Π° скалярноС ΠΏΡ€ΠΎΠΈΠ·Π²Π΅Π΄Π΅Π½ΠΈΠ΅, Ρ‚ΠΎ ΠΊΠ°ΠΊ Ρ€Π°Π· ΠΈ высчитывали эту ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΡƒ. Π§Π΅ΠΌ ΠΎΠ½Π° мСньшС, Ρ‚Π΅ΠΌ большС ΠΏΠΎΡ…ΠΎΠΆΠ΅ΡΡ‚ΡŒ.

ΠšΠΎΡΠΈΠ½ΡƒΡΠ½Π°Ρ Π±Π»ΠΈΠ·ΠΎΡΡ‚ΡŒ

ΠœΠΎΡ‰ΡŒ трансформСров

Π’Π΅ΠΊΡ‚ΠΎΡ€Π° word2vec ΠΈΠ»ΠΈ ΠΈΡ… Π±ΠΎΠ»Π΅Π΅ соврСмСнныС Π°Π½Π°Π»ΠΎΠ³ΠΈ Ρ‚ΠΈΠΏΠ° fasttext ΡΡ‚Ρ€Π°Π΄Π°ΡŽΡ‚ отсутствиСм ΠΊΠΎΠ½Ρ‚Π΅ΠΊΡΡ‚ΡƒΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ. Π’Π°ΠΊΠΆΠ΅ эти эмбСддинги ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Ρ… слов (Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠ²), Π° Π½Π°ΠΌ Π½ΡƒΠΆΠ΅Π½ эмбСдинг всСго Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°.

Π—ΠΎΠΎΠΏΠ°Ρ€ΠΊ трансформСров

Π’ΠΎΡ‚ Ρ‚ΡƒΡ‚ ΠΈ приходят Π½Π° ΠΏΠΎΠΌΠΎΡ‰ΡŒ трансформСры. Π― Π½Π΅ Π±ΡƒΠ΄Ρƒ Ρ€Π°ΡΡΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ ΠΏΡ€ΠΎ ΠΈΡ… ΠΊΡ€ΡƒΡ‚ΠΎΡΡ‚ΡŒ ΠΈ магию, ΠΏΡ€ΠΎΠΈΡΡ…ΠΎΠ΄ΡΡ‰ΡƒΡŽ ΠΏΠΎΠ΄ ΠΊΠ°ΠΏΠΎΡ‚ΠΎΠΌ, Ρ‚Π΅ΠΌ Π±ΠΎΠ»Π΅Π΅ Ρ‡ΠΈΡ‚Π°Ρ‚Π΅Π»ΡŒ Π½Π΅Π΄ΠΎΡƒΠΌΠ΅Π²Π°Π΅Ρ‚ ΠΈ навСрняка ΠΆΠ΄Π΅Ρ‚, ΠΊΠΎΠ³Π΄Π° ΠΆΠ΅ я всС-Ρ‚Π°ΠΊΠΈ Π½Π°Ρ‡Π½Ρƒ Ρ€Π°ΡΡΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ ΠΏΡ€ΠΎ ChatGPT.

Π‘Π°ΠΌΠΎΠ΅ Π³Π»Π°Π²Π½ΠΎΠ΅, Ρ‡Ρ‚ΠΎ ΠΈΠ· энкодСра трансформСра ΠΌΠΎΠΆΠ½ΠΎ Π²Ρ‹Ρ‚Π°Ρ‰ΠΈΡ‚ΡŒ ΠΊΠ°ΠΊ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Π΅ эмбСддинги слов (ΠΏΡ€ΠΈ Ρ‡Π΅ΠΌ здСсь ΠΎΠ½ΠΈ Π±ΡƒΠ΄ΡƒΡ‚ ΡƒΠΆΠ΅ контСкстно-зависимыми), Ρ‚Π°ΠΊ ΠΈ Π²Π΅ΠΊΡ‚ΠΎΡ€Π½ΠΎΠ΅ прСдставлСниС прСдлоТСния.

На ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ΅ ΠΎΡ‡Π΅Π½ΡŒ Ρ…ΠΎΡ€ΠΎΡˆΠΎ сСбя ΠΏΠΎΠΊΠ°Π·Π°Π» Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ SentenceTransformers. Π‘ Π΅Π³ΠΎ ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΌΠΎΠΆΠ½ΠΎ Π²Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ ваш Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ нСсколькими строчками ΠΊΠΎΠ΄Π°:

SentenceTransformers
from sentence_transformers import SentenceTransformer
# ΠΈΠΌΠΏΠΎΡ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ ΠΌΡƒΠ»ΡŒΡ‚ΠΈΡΠ·Ρ‹Ρ‡Π½ΡƒΡŽ модСль
model = SentenceTransformer('distiluse-base-multilingual-cased-v2')
sentence = 'Мама ΠΌΡ‹Π»Π° Ρ€Π°ΠΌΡƒ'
# ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ эмбСддинг
embedding = model.encode(sentence)

SentenceTransformers отличная ΡˆΡ‚ΡƒΠΊΠ°, Π½ΠΎ, ΠΊΠ°ΠΊ ΠΈ всС Ρ…ΠΎΡ€ΠΎΡˆΠ΅Π΅, ΠΈΠΌΠ΅Π΅Ρ‚ свои ΠΈΠ·ΡŠΡΠ½Ρ‹. Π“Π»Π°Π²Π½Ρ‹ΠΉ ΠΈΠ· Π½ΠΈΡ… – ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅ Π½Π° Π΄Π»ΠΈΠ½Ρƒ ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ. По ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ это 128 Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠ². МоТно Π² ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΠ΅ ΡƒΠ²Π΅Π»ΠΈΡ‡ΠΈΡ‚ΡŒ это Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π²ΠΏΠ»ΠΎΡ‚ΡŒ Π΄ΠΎ 512, Π½ΠΎ этого всС Ρ€Π°Π²Π½ΠΎ Π½Π΅ Ρ…Π²Π°Ρ‚ΠΈΡ‚, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π²Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ большой тСкст. ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Π½ΠΈΠΆΠ΅ ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ ΠΏΡ€ΠΈ этом происходит.

ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅ Π½Π° количСство Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠ²
sentence_1 = 'Мама ΠΌΡ‹Π»Π° Ρ€Π°ΠΌΡƒ' * 64
sentence_2 = 'Мама ΠΌΡ‹Π»Π° Ρ€Π°ΠΌΡƒ' * 128
embeddings = model.encode([sentence_1, sentence_2])
print((embeddings[0] == embeddings[1]).all())
True

На сцСну Π²Ρ‹Ρ…ΠΎΠ΄ΠΈΡ‚ OpenAI

НаконСц-Ρ‚ΠΎ ΠΌΡ‹ Π΄ΠΎΠ±Ρ€Π°Π»ΠΈΡΡŒ Π΄ΠΎ основного блюда. Команда OpenAI ΠΏΠΎΠΌΠΈΠΌΠΎ web-интСрфСйса, Ρ‚Π°ΠΊΠΆΠ΅ ΠΎΡ‚ΠΊΡ€Ρ‹Π»Π° доступ ΠΊ API своих ΠΌΠΎΠ΄Π΅Π»Π΅ΠΊ. Π’ Ρ‚ΠΎΠΌ числС ΠΌΠΎΠΆΠ½ΠΎ Π·Π°Π΄Π΅ΠΉΡΡ‚Π²ΠΎΠ²Π°Ρ‚ΡŒ ΠΈ Ρ‚Π΅, Ρ‡Ρ‚ΠΎ ΡΠΎΠ·Π΄Π°ΡŽΡ‚ тСкстовыС эмбСддинги. Π›ΠΎΠ³ΠΈΠΊΠ° Ρ‚ΡƒΡ‚ такая ΠΆΠ΅, ΠΊΠ°ΠΊ ΠΈ Π² SentenceTransformers, Π½ΠΎ ΠΌΡ‹ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Π΅ΠΌ, Ρ‡Ρ‚ΠΎ Ρ‚Π°ΠΊΠΈΠ΅ Π²Π΅ΠΊΡ‚ΠΎΡ€Π°ΠΉΠ·Π΅Ρ€Ρ‹ задСйствуСт Π³ΠΎΡ€Π°Π·Π΄ΠΎ больший контСкст.

Если ΠΎΠ±Ρ€Π°Ρ‚ΠΈΡ‚ΡŒΡΡ ΠΊ Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ, Ρ‚ΠΎ ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎ максимальная Π΄Π»ΠΈΠ½Π° Π²Ρ…ΠΎΠ΄Π½ΠΎΠΉ ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ сущСствСнно Π²Ρ‹ΡˆΠ΅, Ρ‡Π΅ΠΌ Ρƒ SentenceTransformers.

ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ ΠΌΠΎΠ΄Π΅Π»ΠΈ ada ΠΎΡ‚ OpenAI

ΠŸΠΎΡ€Π° ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ, ΠΊΠ°ΠΊ ΠΎΠ½ΠΈ справятся с Π·Π°Π΄Π°Ρ‡Π΅ΠΉ поиска Π΄ΡƒΠ±Π»Π΅ΠΉ.

  1. Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ Π²ΠΈΡ€Ρ‚ΡƒΠ°Π»ΡŒΠ½ΠΎΠ΅ ΠΎΠΊΡ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ ΠΏΠΎΠ΄ этот ΠΏΡ€ΠΎΠ΅ΠΊΡ‚
Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π²ΠΈΡ€Ρ‚ΡƒΠ°Π»ΡŒΠ½ΠΎΠ³ΠΎ окруТСния
conda create --name open_ai
conda activate open_ai
  1. УстанавливаСм Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹Π΅ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ
Установка Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ
pip install openai tiktoken juputer scipy pandas fastapi uvicorn

Для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с openai Π½Π°ΠΌ понадобится Ρ‚ΠΎΠΊΠ΅Π½ Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ.

  1. РСгистрируСмся Π½Π° сайтС.
  2. Π“Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅ΠΌ ΠΊΠ»ΡŽΡ‡.
  3. Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€ΠΎΡ‚Π΅ΡΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ.
ВСстированиС эмбСддингов OpenAI
import tiktoken
import openai
from scipy.spatial.distance import cosine
# УстанавливаСм Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΊΠ»ΡŽΡ‡Π°
openai.api_key = 'sk-7FUL3uCuviG7F...Kc08xiSoFidB9Us'
# ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ ΠΏΡ€Π΅Π΄Π»ΠΎΠΆΠ΅Π½ΠΈΠΉ
sentence_1 = 'ΠΌΠ°ΠΌΠ° ΠΌΡ‹Π»Π° Ρ€Π°ΠΌΡƒ'
sentence_2 = 'ΠΌΠ°ΠΌΠ° ΠΌΠΎΠ΅Ρ‚ ΠΎΠΊΠ½ΠΎ'
# ВСрсия ΠΌΠΎΠ΄Π΅Π»ΠΈ(это Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅ соврСмСнная)
model = 'text-embedding-ada-002'
# БоздаСм эмбСддинги
embedding_1 = openai.Embedding.create( input=sentence_1, model=model)
embedding_2 = openai.Embedding.create( input=sentence_2, model=model)
# Π‘ΠΌΠΎΡ‚Ρ€ΠΈΠΌ Π½Π° Π±Π»ΠΈΠ·ΠΎΡΡ‚ΡŒ, Π² Π΄Π°Π½Π½ΠΎΠΌ случаС считаСтся ΠΊΠ°ΠΊ 1-cos
distance = cosine(embedding_1['data'][0]['embedding'], embedding_2['data'][0]['embedding'])
print(distance)
0.11193540954120007

ΠŸΠΎΡΠΌΠΎΡ‚Ρ€ΠΈΠΌ, ΠΊΠ°ΠΊ ΠΎΠ½Π° справляСтся с Π΄Π»ΠΈΠ½Π½Ρ‹ΠΌΠΈ ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡΠΌΠΈ.

ВСстированиС эмбСддингов OpenAI Π½Π° Π΄Π»ΠΈΠ½Π½Ρ‹Ρ… тСкстах
sentence_1 = '''
Π’ январС Π“Π΅Π½Π΅Ρ€Π°Π»ΡŒΠ½Π°Ρ ΠΏΡ€ΠΎΠΊΡƒΡ€Π°Ρ‚ΡƒΡ€Π° Π“Π΅Ρ€ΠΌΠ°Π½ΠΈΠΈ Π² Ρ€Π°ΠΌΠΊΠ°Ρ… расслСдования дивСрсии Π½Π° Π³Π°Π·ΠΎΠΏΡ€ΠΎΠ²ΠΎΠ΄Π°Ρ… Β«Π‘Π΅Π²Π΅Ρ€Π½Ρ‹ΠΉ ΠΏΠΎΡ‚ΠΎΠΊΒ» ΠΏΡ€ΠΎΠ²ΠΎΠ΄ΠΈΠ»Π° обыск
Π½Π° ΠΏΠΎΠ΄ΠΎΠ·Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΌ суднС, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ·ΠΈΠ»ΠΎ Π²Π·Ρ€Ρ‹Π²Ρ‡Π°Ρ‚ΠΊΡƒ, ΠΏΠΈΡˆΠ΅Ρ‚ Die Welt со ссылкой Π½Π° заявлСниС вСдомства.
ΠŸΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎΡΡ‚Π΅ΠΉ, ΠΊΡ‚ΠΎ ΠΈ Π·Π°Ρ‡Π΅ΠΌ ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ·ΠΈΠ» Π΅Π΅, вСдомство Π½Π΅ ΠΏΡ€ΠΈΠ²ΠΎΠ΄ΠΈΡ‚. Π’ ΠΎΡ‚Π²Π΅Ρ‚ Π½Π° запрос ВАББ Π² Π“Π΅Π½ΠΏΡ€ΠΎΠΊΡƒΡ€Π°Ρ‚ΡƒΡ€Π΅ ΡƒΠΊΠ°Π·Π°Π»ΠΈ, Ρ‡Ρ‚ΠΎ ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°ΡŽΡ‚
Π°Π½Π°Π»ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΡƒΠ»ΠΈΠΊΠΈ ΠΈ Π½Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π³ΠΎΠ²ΠΎΡ€ΠΈΡ‚ΡŒ ΠΎ причастности ΠΊ ΠΏΠΎΠ΄Ρ€Ρ‹Π²Ρƒ ΠΊΠ°ΠΊΠΎΠ³ΠΎ-Π»ΠΈΠ±ΠΎ государства. Π‘ΡƒΠ΄Π½ΠΎ, ΠΏΠΎ Π΄Π°Π½Π½Ρ‹ΠΌ вСдомства,
Π±Ρ‹Π»ΠΎ Π°Ρ€Π΅Π½Π΄ΠΎΠ²Π°Π½ΠΎ Ρƒ ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ ΠΈΠ· Π€Π Π“, Π½ΠΎ Π΅Π΅ сотрудники Π²Π½Π΅ ΠΏΠΎΠ΄ΠΎΠ·Ρ€Π΅Π½ΠΈΠΉ.
Π Π°Π½Π΅Π΅ Π³Π°Π·Π΅Ρ‚Π° Die Zeit сообщила, Ρ‡Ρ‚ΠΎ Π½Π΅ΠΌΠ΅Ρ†ΠΊΠΈΠ΅ слСдствСнныС ΠΎΡ€Π³Π°Π½Ρ‹ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΠΎΠ²Π°Π»ΠΈ яхту,
которая, вСроятно, использовалась ΠΏΡ€ΠΈ ΡΠΎΠ²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠΈ Π²Π·Ρ€Ρ‹Π²Π° Π½Π° Β«Π‘Π΅Π²Π΅Ρ€Π½Ρ‹Ρ… ΠΏΠΎΡ‚ΠΎΠΊΠ°Ρ…Β» 26 сСнтября ΠΏΡ€ΠΎΡˆΠ»ΠΎΠ³ΠΎ Π³ΠΎΠ΄Π°.
'''
sentence_2 = '''
Власти Π€Π Π“ ΠΈΠ½ΠΈΡ†ΠΈΠΈΡ€ΠΎΠ²Π°Π»ΠΈ обыск корабля, ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΠΈΠΌΠ΅ΡŽΡ‰Π΅Π³ΠΎ ΠΎΡ‚Π½ΠΎΡˆΠ΅Π½ΠΈΠ΅ ΠΊ дивСрсиям Π½Π° Π³Π°Π·ΠΎΠΏΡ€ΠΎΠ²ΠΎΠ΄Π°Ρ… Β«Π‘Π΅Π²Π΅Ρ€Π½Ρ‹ΠΉ ΠΏΠΎΡ‚ΠΎΠΊΒ»
ΠΈ Β«Π‘Π΅Π²Π΅Ρ€Π½Ρ‹ΠΉ ΠΏΠΎΡ‚ΠΎΠΊ – 2Β». Об этом 8 ΠΌΠ°Ρ€Ρ‚Π° сообщаСт агСнтство DPA со ссылкой Π½Π° Π“Π΅Π½Π΅Ρ€Π°Π»ΡŒΠ½ΡƒΡŽ ΠΏΡ€ΠΎΠΊΡƒΡ€Π°Ρ‚ΡƒΡ€Ρƒ Π² ΠšΠ°Ρ€Π»ΡΡ€ΡƒΡ.
Богласно ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ вСдомства, судно ΠΌΠΎΠ³Π»ΠΎ Π±Ρ‹Ρ‚ΡŒ использовано для ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ·ΠΊΠΈ Π²Π·Ρ€Ρ‹Π²Ρ‡Π°Ρ‚Ρ‹Ρ… вСщСств.
Β«Π‘ 18 ΠΏΠΎ 20 января 2023 Π³ΠΎΠ΄Π° Ρ„Π΅Π΄Π΅Ρ€Π°Π»ΡŒΠ½Π°Ρ ΠΏΡ€ΠΎΠΊΡƒΡ€Π°Ρ‚ΡƒΡ€Π° обыскала судно Π² связи с ΠΏΠΎΠ΄ΠΎΠ·Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ Π΅Π³ΠΎ Π°Ρ€Π΅Π½Π΄ΠΎΠΉ.
Π•ΡΡ‚ΡŒ ΠΏΠΎΠ΄ΠΎΠ·Ρ€Π΅Π½ΠΈΠ΅, Ρ‡Ρ‚ΠΎ рассматриваСмоС судно ΠΌΠΎΠ³Π»ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ для ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ·ΠΊΠΈ Π²Π·Ρ€Ρ‹Π²Π½Ρ‹Ρ… устройств,
Π²Π·ΠΎΡ€Π²Π°Π²ΡˆΠΈΡ…ΡΡ 26 сСнтября 2022 Π³ΠΎΠ΄Π° Π½Π° Π³Π°Π·ΠΎΠΏΡ€ΠΎΠ²ΠΎΠ΄Π°Ρ… Β«Π‘Π΅Π²Π΅Ρ€Π½Ρ‹ΠΉ ΠΏΠΎΡ‚ΠΎΠΊΒ» 1 ΠΈ 2 Π² Балтийском ΠΌΠΎΡ€Π΅Β»,
β€” сообщили Π² ΠΏΡ€ΠΎΠΊΡƒΡ€Π°Ρ‚ΡƒΡ€Π΅ агСнтству «РИА Новости».
ΠžΡ‚ΠΌΠ΅Ρ‡Π°Π΅Ρ‚ΡΡ, Ρ‡Ρ‚ΠΎ Π½Π° Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ ΠΈΠ·ΡŠΡΡ‚ ряд Π²Π΅Ρ‰Π΄ΠΎΠΊΠΎΠ², проводится ΠΈΡ… ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ°. Личности прСступников
ΠΈ ΠΈΡ… ΠΌΠΎΡ‚ΠΈΠ²Ρ‹ ΡΠ²Π»ΡΡŽΡ‚ΡΡ ΠΏΡ€Π΅Π΄ΠΌΠ΅Ρ‚ΠΎΠΌ ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°ΡŽΡ‰ΠΈΡ…ΡΡ расслСдований.
«ДостовСрных Π΄ΠΎΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒΡΡ‚Π² ΠΏΠΎ этому ΠΏΠΎΠ²ΠΎΠ΄Ρƒ, особСнно ΠΏΠΎ вопросу государствСнного контроля,
Π² настоящСС врСмя Π½Π΅Ρ‚. Никаких ΠΏΠΎΠ΄ΠΎΠ·Ρ€Π΅Π½ΠΈΠΉ Π² ΠΎΡ‚Π½ΠΎΡˆΠ΅Π½ΠΈΠΈ сотрудников Π½Π΅ΠΌΠ΅Ρ†ΠΊΠΎΠΉ ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ,
сдавшСй судно Π² Π°Ρ€Π΅Π½Π΄Ρƒ, Π½Π΅Ρ‚Β», β€” Π·Π°ΠΊΠ»ΡŽΡ‡ΠΈΠ»ΠΈ Π² ΠΏΡ€ΠΎΠΊΡƒΡ€Π°Ρ‚ΡƒΡ€Π΅.

'''
# Π­Π½ΠΊΠΎΠ΄Π΅Ρ€ для подсчСта количСства Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠ²
encoding = tiktoken.get_encoding('cl100k_base')

num_tokens_1 = len(encoding.encode(sentence_1))
num_tokens_2 = len(encoding.encode(sentence_2))

embedding_1 = openai.Embedding.create(
  input=sentence_1,
  model=model
)

embedding_2 = openai.Embedding.create(
  input=sentence_2,
  model=model
)

distance = cosine(
	embedding_1['data'][0]['embedding'],
	embedding_2['data'][0]['embedding']
)

print(
    f'sentence_1 num tokens: {num_tokens_1}\\n'
    f'sentence_2 num tokens: {num_tokens_2}\\n'
    f'cos distance: {distance}'
)

sentence_1 num tokens: 347
sentence_2 num tokens: 492
cos distance: 0.06688715737425832

Для ΠΏΠΎΡ…ΠΎΠΆΠΈΡ… ΠΏΠΎ смыслу новостСй, Π½ΠΎ с Ρ€Π°Π·Π½ΠΎΠΉ лСксикой ΠΈ Ρ€Π°Π·Π½Ρ‹ΠΌ Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠΌ, модСль Π²Ρ‹Π΄Π°Π΅Ρ‚ Π°Π΄Π΅ΠΊΠ²Π°Ρ‚Π½ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ косинусного расстояния. Π§Ρ‚ΠΎ ΠΆ, Π²ΠΈΠ΄ΠΈΠΌΠΎ, рСбята ΠΈΠ· ΠšΠ°Π»ΠΈΡ„ΠΎΡ€Π½ΠΈΠΈ всС-Ρ‚Π°ΠΊΠΈ Π½Π΅ зря Сдят свой Ρ…Π»Π΅Π± со смузи.

По коням

ΠŸΠΎΡ€Π° ΡΠΎΠ±ΠΈΡ€Π°Ρ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‰ΠΈΠΉ ΠΏΠ°ΠΉΠΏΠ»Π°ΠΉΠ½. НСобходимыС ΠΈΠ½Π³Ρ€ΠΈΠ΄ΠΈΠ΅Π½Ρ‚Ρ‹:

  1. ΠšΠΎΡ€ΠΏΡƒΡ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹Ρ… тСкстов. Π§Ρ‚ΠΎΠ±Ρ‹ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ наш ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π½Π° ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ, ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΡΡ€Π°Π²Π½ΠΈΠ²Π°Ρ‚ΡŒ Π΅Π³ΠΎ с ΠΊΠ°ΠΆΠ΄Ρ‹ΠΌ Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΎΠΌ ΠΈΠ· корпуса. ΠŸΠ°Ρ€ΡΠΈΡ‚ΡŒ Π½ΠΈΡ‡Π΅Π³ΠΎ Π½Π΅ Π±ΡƒΠ΄Π΅ΠΌ, возьмСм Π³ΠΎΡ‚ΠΎΠ²Ρ‹ΠΉ датасСт с huggingface.
  2. Код для поиска ΠΏΠΎΡ‚Π΅Π½Ρ†ΠΈΠ°Π»ΡŒΠ½Ρ‹Ρ… Π΄ΡƒΠ±Π»Π΅ΠΉ.
  3. API-интСрфСйс для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с нашим микросСрвисом.

На Π²Ρ…ΠΎΠ΄ ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΏΠΎΠ΄Π°Π²Π°Ρ‚ΡŒ список ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠ², Π½Π° Π²Ρ‹Ρ…ΠΎΠ΄Π΅ ΠΎΡ†Π΅Π½ΠΊΠ° ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ:

ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Π²Ρ…ΠΎΠ΄Π½Ρ‹Ρ… ΠΈ Π²Ρ‹Ρ…ΠΎΠ΄Π½Ρ‹Ρ… Π΄Π°Π½Π½Ρ‹Ρ… для API
api_input = {
	  "ITEMS": [
	    doc1,
	    doc2,
	    doc3
	  ]
	}
	
api_output = [
    {
        "estimation": 1,
        "score": 0.3,
        "duplicates": []
    },
    {
        "estimation": 0,
        "score": 0.03,
        "duplicates": [original1, original2]
    },
    {
        "estimation": 1,
        "score": 0.41,
        "duplicates": []
    },
]

ДатасСт

Π‘ΠΊΠ°Ρ‡Π°Π½Π½Ρ‹ΠΉ Π°Ρ€Ρ…ΠΈΠ² вСсит 3.5 Π“Π‘. Π­Ρ‚ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΎΠΊΠ°Π·Π°Ρ‚ΡŒΡΡ ΡΠ΅Ρ€ΡŒΠ΅Π·Π½Ρ‹ΠΌ Π²Ρ‹Π·ΠΎΠ²ΠΎΠΌ для вашСй ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΈΠ²ΠΊΠΈ, ΠΊΡ€ΠΎΠΌΠ΅ Ρ‚ΠΎΠ³ΠΎ, поиск Π² большом корпусС Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°Π½ΠΈΠΌΠ°Ρ‚ΡŒ слишком ΠΌΠ½ΠΎΠ³ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ (сСйчас ΠΏΠΎΠΊΠ° пропустим Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Π΅ способы ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ этого процСсса). БСйчас наша Π·Π°Π΄Π°Ρ‡Π° Π²Ρ‹ΠΊΠ°Ρ‚ΠΈΡ‚ΡŒ MVP (minimal viable product), поэтому ограничимся Π±Π°Ρ‚Ρ‡Π΅ΠΌ Π² 10ΠΊ постов.

Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° датасСта
import pandas as pd
import pickle

# Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ ΠΈΡ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€ для ΠΏΠΎΠ±Π°Ρ‚Ρ‡Π΅Π²ΠΎΠΉ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ, Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ потрСбуСтся установка стандарта zstd(pip install zstandard)
habr = pd.read_json(
	'habr.jsonl.zst',
	lines=True,
	chunksize=10**4,
	compression='zstd'
)
# Π—Π°Π±ΠΈΡ€Π°Π΅ΠΌ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ Π±Π°Ρ‚Ρ‡
habr_chunk = next(habr)

print(habr_chunk.shape)
(10000, 22)
Π₯Π΅Π΄Π΅Ρ€ датасСта

Π’ сСтС 22 ΠΊΠΎΠ»ΠΎΠ½ΠΊΠΈ, Π½ΠΎ Π½Π°ΠΌ ΡΡ‚ΠΎΠ»ΡŒΠΊΠΎ Π½Π΅ понадобится. НаиболСС Π²Π°ΠΆΠ½Ρ‹Π΅ для нас:

  1. text_markdown – собствСнно сам тСкст поста.
  2. lead_markdown – Ρ…Π΅Π΄Π΅Ρ€ поста (ΠΏΠ΅Ρ€Π²Ρ‹Π΅ нСсколько ΠΏΡ€Π΅Π΄Π»ΠΎΠΆΠ΅Π½ΠΈΠΉ).
  3. title – Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ.

Для поиска Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚ΠΎΠ² ΠΏΠΎΠΊΠ° ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡ΠΈΡ‚ΡŒΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ text_markdown, Π² дальнСйшСм ΠΌΠΎΠΆΠ½ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°Π΄Π΅ΠΉΡΡ‚Π²ΠΎΠ²Π°Ρ‚ΡŒ ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ поля.

ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ эмбСддингов

Π’Ρ‹ΡˆΠ΅ я ΡƒΠΊΠ°Π·Ρ‹Π²Π°Π», Ρ‡Ρ‚ΠΎ модСль способна ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Ρ‚ΡŒ Π½Π° Π²Ρ…ΠΎΠ΄ ΡΠ²Ρ‹ΡˆΠ΅ 8k Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠ², ΠΎΠ΄Π½Π°ΠΊΠΎ Ссли ваш тСкст прСвысит это Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, Ρ‚ΠΎ API ΡƒΠΏΠ°Π΄Π΅Ρ‚ с ошибкой. ΠŸΠ΅Ρ€Π΅Π΄ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΎΠΉ Π±ΡƒΠ΄Π΅ΠΌ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚ΡŒ подсчСт ΠΈ ΠΎΠ±Ρ€Π΅Π·Π°Ρ‚ΡŒ наши тСксты.

Ѐункция для получСния эмбСддингов
def get_embeddings(  
        texts: list,  
        model: str = 'text-embedding-ada-002',  
        chunksize: int = 100,  
        max_tokens: int = 8192,  
        cut_coef: float = 0.8  
) -> list:  
    """  
    
Ѐункция ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ список тСкстов ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ список эмбСддингов, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ    модСль ΠΎΡ‚ OpenAI. Бписок разбиваСтся Π½Π° куски Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠΌ chunksize, Π° Π·Π°Ρ‚Π΅ΠΌ функция вычисляСт количСство    Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠ² ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ тСкста, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ энкодСр cl100k_base. Если количСство Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠ² ΠΏΡ€Π΅Π²Ρ‹ΡˆΠ°Π΅Ρ‚ max_tokens,тСкст укорачиваСтся Π½Π° cut_coef.
  
АргумСнты:
  
    df (pd.DataFrame): pandas DataFrame, содСрТащий тСкстовыС Π΄Π°Π½Π½Ρ‹Π΅
		model (str ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ text-embedding-ada-002): модСль Π²Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ
    chunksize(int ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ 100): Ρ€Π°Π·ΠΌΠ΅Ρ€ кусков, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Ρ€Π°Π·Π±ΠΈΠ²Π°ΡŽΡ‚ΡΡ Π²Ρ…ΠΎΠ΄Π½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅
    max_tokens (int ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ 8192): максимальноС количСство Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠ², Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½Π½ΠΎΠ΅ Π½Π° ΠΎΠ΄ΠΈΠ½ тСкст
    cut_coef (float ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ 0.8): коэффициСнт, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡ‹ΠΉ для опрСдСлСния количСства тСкста для ΠΎΠ±Ρ€Π΅Π·ΠΊΠΈ, Ссли ΠΎΠ½ΠΎ ΠΏΡ€Π΅Π²Ρ‹ΡˆΠ°Π΅Ρ‚ max_tokens
  
Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚:

    список эмбСддингов  
    
    """  
    embeddings_all = []  
    encoding = tiktoken.get_encoding('cl100k_base')  
  
    @retry(wait=wait_random_exponential(min=1, max=10), stop=stop_after_attempt(5))  # ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ запросов  
    def api_request(texts, model):  
        return openai.Embedding.create(input=texts, model=model)  
  
    for i in range(0, len(texts), chunksize):  
        processed_texts = []  
        texts_chunk = texts[i:i + chunksize]  
        for text in texts_chunk:  
            if not text:  
                text = 'default'  
            num_tokens = len(encoding.encode(text))  
            if num_tokens > max_tokens:  
                len_text = len(text)  
                cut_trh = int(max_tokens / num_tokens * len_text * cut_coef)  
                text = text[:cut_trh]  
            processed_texts.append(text)  
  
        embeddings = api_request(processed_texts, model)  
  
        for embedding in embeddings['data']:  
            embeddings_all.append(embedding['embedding'])  
        if (i % 1000 == 0) & (i != 0):  
            print(f'processed {i} texts')  
  
    return embeddings_all

Π­ΠΌΠ±Π΅Π΄Π΄ΠΈΠ½Π³ΠΈ записываСм Π² датасСт ΠΈ сохраняСм.

Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ эмбСддингов
embeddings_all = get_embeddings(habr_chunk.text_markdown.values.tolist())

habr_chunk['embedding'] = embeddings_all

with open('habr_chunk.pickle', 'wb') as f:
    pickle.dump(habr_chunk[['text_markdown', 'embedding']], f

Π‘Π°Π·Π° Π΄Π°Π½Π½Ρ‹Ρ… ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹Ρ… постов Ρƒ нас Π΅ΡΡ‚ΡŒ. ΠŸΡ€Π°Π²Π΄Π°, ΠΌΡ‹ Π½ΠΈΠΊΠ°ΠΊ Π½Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΠ»ΠΈ ΠΈΡ… Π½Π° ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ. Пока ΠΎΡ‚ΠΏΡ€Π°Π²ΠΈΠΌ эту Π·Π°Π΄Π°Ρ‡Ρƒ Π² бэклог.

Поиск Π΄ΡƒΠ±Π»Π΅ΠΉ

Ѐункция поиска Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚ΠΎΠ²
def find_duplicates(candidates: list, df: pd.DataFrame, n_similar: int = 5, thrh: float = 0.1) -> list:
    '''
	Ѐункция Π½Π°Ρ…ΠΎΠ΄ΠΈΡ‚ Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚Ρ‹ тСкстовых Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚ΠΎΠ² для  списка candidates Π½Π° основС сходства эмбСддингов, вычислСнных с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ get_embeddings, ΠΈ Π±Π°Π·Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ… Π² Π²ΠΈΠ΄Π΅ pandas.DataFrame. Ѐункция Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ список, содСрТащий ΠΎΡ†Π΅Π½ΠΊΡƒ, Π±Π»ΠΈΠ·ΠΎΡΡ‚ΡŒ ΠΈ список ΠΏΠΎΡ…ΠΎΠΆΠΈΡ… Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚ΠΎΠ² для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ тСкстового Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚Π° ΠΈΠ· candidates.

 АргумСнты:

    candidates (list): список тСкстовых Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚ΠΎΠ² для поиска Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚ΠΎΠ²
    df (pd.DataFrame): Π±Π°Π·Π° Π΄Π°Π½Π½Ρ‹Ρ… Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ pandas.DataFrame, содСрТащая эмбСддинги ΠΈ тСкстовыС Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚Ρ‹ для сравнСния с candidates
    n_similar (int, ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ 5): количСство ΠΏΠΎΡ…ΠΎΠΆΠΈΡ… Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π±ΡƒΠ΄ΡƒΡ‚ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π΅Π½Ρ‹ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ тСкста candidates
    thrh (float, ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ 0.1): ΠΏΠΎΡ€ΠΎΠ³ΠΎΠ²ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΠΎΠ΅ для опрСдСлСния Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎ Π΄Π²Π° эмбСддинга достаточно ΠΏΠΎΡ…ΠΎΠΆΠΈ.
    
 Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚:

    список словарСй с ΠΎΡ†Π΅Π½ΠΊΠΎΠΉ, косинусной Π±Π»ΠΈΠ·ΠΎΡΡ‚ΡŒΡŽ ΠΈ списком Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚ΠΎΠ².
    '''
    embeddings = get_embeddings(candidates)
    processed_candidates = []
    for embedding in embeddings:
        checked_candidat = {
            "estimation": 0,
            "score": None,
            "duplicates": []
        }
        distances = np.array(  
            [cosine(embedding, e) for e in df.embedding.values]  
        )
        under_treshold = distances < thrh
        if under_treshold.sum() == 0:  
            processed_candidates.append(checked_candidat)  
        else: 
            checked_candidat["estimation"] = 1
            checked_candidat["score"] = min(distances)
            checked_candidat["duplicates"] = df.iloc[
                under_treshold
            ].text_markdown.values[:n_similar].tolist()
            
        processed_candidates.append(checked_candidat)
        
    return processed_candidates

ΠœΠΈΠΊΡ€ΠΎΡΠ΅Ρ€Π²ΠΈΡ крутится

ВсС Π΄Π΅Ρ‚Π°Π»ΡŒΠΊΠΈ микросСрвиса Π³ΠΎΡ‚ΠΎΠ²Ρ‹, ΠΎΡΡ‚Π°Π»ΠΎΡΡŒ ΡΠΎΠ΅Π΄ΠΈΠ½ΠΈΡ‚ΡŒ ΠΈΡ… вмСстС ΠΈ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ FastAPI. Для удобства создадим ΠΏΠ°ΠΊΠ΅Ρ‚ utils, Π² Π½Π΅Π³ΠΎ помСстим ΠΊΠΎΠ΄ для получСния Π²Π΅ΠΊΡ‚ΠΎΡ€ΠΎΠ² ΠΈ поиска ΠΏΠΎ постам.

Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΏΠ°ΠΊΠ΅Ρ‚Π° utils
mkdir utils
touch utils/__init__.py utils/search.py app.py

Π’ search.py скопируСм ΠΊΠΎΠ΄ для эмбСддингов ΠΈ поиска, Π² app.py ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ FastAPI, ΠΎΠ½ΠΎ ΠΏΡ€Π΅Π΄Π΅Π»ΡŒΠ½ΠΎ простоС.

Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Ρ„Π°ΠΉΠ»Π° app.py с FastAPI
import os  
import pickle  
  
from fastapi import FastAPI  
from pydantic import BaseModel  
  
import openai  
  
from utils.search import find_duplicates  
  
# УстанавливаСм Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΊΠ»ΡŽΡ‡Π°(Ρ‚Π°ΠΊ Π»ΡƒΡ‡ΡˆΠ΅ Π½Π΅ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ!!!)
openai.api_key = 'sk-7F...idB9Us'  
  
# НазваниС Ρ„Π°ΠΉΠ»Π° с ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΌΠΈ постами  
habr_masters_path = 'habr_chunk.pickle'  
  
# Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ экзСмпляр fastapi  
app = FastAPI(title='Deduplication App')  
  
# Для Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ Π΄Π°Π½Π½Ρ‹Ρ… создаСм модСль Π΄Π°Π½Π½Ρ‹Ρ… 
class Candidat(BaseModel):  
    ITEM: list  
   
# ΠŸΠΎΠ΄Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌ Π΄Π°Ρ‚Π°Ρ„Ρ€Π΅ΠΉΠΌ с постами  
with open(os.path.join(os.path.dirname(__file__), habr_masters_path), 'rb') as f:  
    df = pickle.load(f)  

# ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ для входящих сообщСний
@app.post('/predict')  
async def make_predictions(request: Candidat):  
    if not request.ITEM:  
        request.ITEM.append('dafault')  
    processed_candidates = find_duplicates(request.ITEM, df)  
    return processed_candidates

Done!

Π§Ρ‚ΠΎΠ±Ρ‹ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, запускаСм сСрвСр uvicorn.

Запуск прилоТСния
uvicorn app:app --reload

Π’ случаС успСха Π²Ρ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ ΠΏΠΎΡ…ΠΎΠΆΠ΅Π΅ Π½Π° это:

Π’Ρ‹Π²ΠΎΠ΄ Π² консоль Π»ΠΎΠ³ΠΎΠ² uvicorn
INFO:     Will watch for changes in these directories: ['/Users/m.konakov/Desktop/search_duplicates']
INFO:     Uvicorn running on <http://127.0.0.1:8000> (Press CTRL+C to quit)
INFO:     Started reloader process [24198] using StatReload
INFO:     Started server process [24200]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

ΠšΡ€ΡƒΡ‚ΠΎΡΡ‚ΡŒ FastAPI Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π² быстротС, Π½ΠΎ ΠΈ Π² удобствС ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ. ΠŸΠ΅Ρ€Π΅ΠΉΠ΄Ρ ΠΏΠΎ адрСсу http://127.0.0.1:8000/docs, Π²Ρ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΠΏΠΎΠΏΠ°ΡΡ‚ΡŒ Π² swagger. Swagger – это Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ, ΠΏΡ€Π΅Π΄ΠΎΡΡ‚Π°Π²Π»ΡΡŽΡ‰ΠΈΠΉ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΡ€ΠΎΡΠΌΠ°Ρ‚Ρ€ΠΈΠ²Π°Ρ‚ΡŒ ΡΠΏΠ΅Ρ†ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΡŽ RESTful API ΠΈΠ½Ρ‚Π΅Ρ€Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎ, Π½ΠΎ ΠΈ ΠΎΡ‚ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ запросы ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ ΠΎΡ‚Π²Π΅Ρ‚Ρ‹.

Swagger FastAPI

Π’ список ITEM ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠΌΠ΅Ρ‰Π°Ρ‚ΡŒ своих ΠΊΠ°Π½Π΄ΠΈΠ΄Π°Ρ‚ΠΎΠ² Π½Π° Π΄ΡƒΠ±Π»ΠΈ. Π’ΠΎΠ·ΡŒΠΌΠ΅ΠΌ, для ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°, Ρ…Π΅Π΄Π΅Ρ€ ΠΊΠ°ΠΊΠΎΠ³ΠΎ-Π½ΠΈΠ±ΡƒΠ΄ΡŒ поста. По-Ρ…ΠΎΡ€ΠΎΡˆΠ΅ΠΌΡƒ, стоит ΠΆΠ΄Π°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎ модСль Π΄ΠΎΠ»ΠΆΠ½Π° Π½Π°ΠΌ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ ΠΏΠΎΠ»Π½Ρ‹ΠΉ тСкст Π² качСствС Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚Π°.

ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ ΠΏΡ€Π΅Π΄ΠΈΠΊΡ‚ΠΎΠ² ΠΌΠΎΠ΄Π΅Π»ΠΈ Π² swagger

К ΡΡ‡Π°ΡΡ‚ΡŒΡŽ, наши оТидания Π½Π΅ оказались нашими ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°ΠΌΠΈ.

Π—Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅

ДСдубликация понятная ΠΈ Π½Π° ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ взгляд довольно простая ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°. Но Ρ€Π΅ΡˆΠ°Ρ Π΅Π΅, ΠΌΠΎΠΆΠ½ΠΎ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ для сСбя мноТСство ΠΊΡ€ΡƒΡ‚Ρ‹Ρ… ΠΈ Ρ†Π΅Π½Π½Ρ‹Ρ… ΠΈΠ΄Π΅ΠΉ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹ Π²ΠΎ мноТСствС Π΄Ρ€ΡƒΠ³ΠΈΡ… Π·Π°Π΄Π°Ρ‡. Π’ ΡΡ‚Π°Ρ‚ΡŒΠ΅ я постарался Ρ€Π°ΡΠΊΡ€Ρ‹Ρ‚ΡŒ Π±Π°Π·ΠΎΠ²Ρ‹Π΅ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Ρ‹, Π±Π΅Π· сущСствСнного погруТСния Π² Π΄Π΅Ρ‚Π°Π»ΠΈ, Π½ΠΎ надСюсь, Ρ‡Ρ‚ΠΎ ΠΊΠΎΠ³ΠΎ-Ρ‚ΠΎ это Π²Π΄ΠΎΡ…Π½ΠΎΠ²ΠΈΡ‚ ΠΊ собствСнным поискам. Бпасибо Π·Π° Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅!

***

ΠœΠ°Ρ‚Π΅Ρ€ΠΈΠ°Π»Ρ‹ ΠΏΠΎ Ρ‚Π΅ΠΌΠ΅

Π›Π£Π§Π¨Π˜Π• БВАВЬИ ПО Π’Π•ΠœΠ•

Π‘ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° программиста
16 ноября 2019

DeepFake-Ρ‚ΡƒΡ‚ΠΎΡ€ΠΈΠ°Π»: создаСм собствСнный Π΄ΠΈΠΏΡ„Π΅ΠΉΠΊ Π² DeepFaceLab

РассказываСм ΠΎ Ρ‚Π΅Ρ…Π½ΠΎΠ»ΠΎΠ³ΠΈΠΈ DeepFake ΠΈ шаг Π·Π° шагом учимся Π΄Π΅Π»Π°Ρ‚ΡŒ Π΄ΠΈΠΏΡ„Π΅ΠΉΠΊΠΈ Π² ...
admin
11 дСкабря 2018

ООП Π½Π° Python: ΠΊΠΎΠ½Ρ†Π΅ΠΏΡ†ΠΈΠΈ, ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΡ‹ ΠΈ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ

ΠŸΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π° Python допускаСт Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Π΅ ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ»ΠΎΠ³ΠΈΠΈ, Π½ΠΎ Π² Π΅Π³ΠΎ основС...
admin
14 июля 2017

ПишСм свою Π½Π΅ΠΉΡ€ΠΎΡΠ΅Ρ‚ΡŒ: пошаговоС руководство

ΠžΡ‚Π»ΠΈΡ‡Π½Ρ‹ΠΉ Π³Π°ΠΉΠ΄ ΠΏΡ€ΠΎ Π½Π΅ΠΉΡ€ΠΎΡΠ΅Ρ‚ΡŒ ΠΎΡ‚ Ρ‚Π΅ΠΎΡ€ΠΈΠΈ ΠΊ ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ΅. Π’Ρ‹ ΡƒΠ·Π½Π°Π΅Ρ‚Π΅ ΠΈΠ· ΠΊΠ°ΠΊΠΈΡ… элСмС...