Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix some tokenization issues #199

Merged
merged 2 commits into from
Aug 14, 2021

Conversation

bhnum
Copy link
Contributor

@bhnum bhnum commented Aug 11, 2021

هنگام کار کردن با کلاس WordTokenizer به شکل زیر:

tokenizer = hazm.WordTokenizer(join_verb_parts=False, separate_emoji=True,
                               replace_links=True, replace_IDs=True,
                               replace_emails=True, replace_numbers=True)

چند مشکل مشاهده کردم که تعدادی از آن‌ها مربوط به قرار داشتن توکن‌های عددی و id در ابتدا یا انتهای رشته بودند که regex های مربوط به آن‌ها نمی‌توانستند این توکن‌ها را تشخیص دهند. هم‌چنین عددهای اعشاری و قسمت انتهایی ایمیل به عنوان لینک تشخیص داده می‌شود و در برخی موارد لینک هم به طور کامل جایگزین نمی‌شود. برای بررسی این مشکل‌ها تست کیس‌های زیر را بررسی کردم:

برای مشاهده کلیک کنید
print(f"{tokenizer.tokenize('123 ') = }")
print(f"{tokenizer.tokenize(' 123 ') = }")
print(f"{tokenizer.tokenize('123') = }")
print(f"{tokenizer.tokenize(' 12343434343433 ') = }")

print(f"{tokenizer.tokenize('۱۲۳ ') = }")
print(f"{tokenizer.tokenize(' ۱۲۳ ') = }")
print(f"{tokenizer.tokenize('۱۲۳') = }")
print(f"{tokenizer.tokenize(' ۱۱۲۳۲۳۴۲۴۱۲۲۳ ') = }")

print(f"{tokenizer.tokenize('123 -2') = }")
print(f"{tokenizer.tokenize('123+ 2') = }")
print(f"{tokenizer.tokenize('123*2') = }")

print(f"{tokenizer.tokenize('0.12') = }")
print(f"{tokenizer.tokenize('۰٫۱۲') = }")
print(f"{tokenizer.tokenize('۱۲٬۰۰۰') = }")
print(f"{tokenizer.tokenize('0.com') = }")
print(f"{tokenizer.tokenize('google.com') = }")
print(f"{tokenizer.tokenize('colab.research.google.com') = }")
print(f"{tokenizer.tokenize('http://abcd.com') = }")
print(f"{tokenizer.tokenize('example.com/index.html?q=hello%20world') = }")

print(f"{tokenizer.tokenize('abcd@gmail.com') = }")
print(f"{tokenizer.tokenize('@fardin') = }")
print(f"{tokenizer.tokenize(' @fardin') = }")

که برای کد کنونی این خروجی را ایجاد می‌کند:

tokenizer.tokenize('123 ') = ['123']
tokenizer.tokenize(' 123 ') = ['NUM3']
tokenizer.tokenize('123') = ['123']
tokenizer.tokenize(' 12343434343433 ') = ['NUM14']
tokenizer.tokenize('۱۲۳ ') = ['۱۲۳']
tokenizer.tokenize(' ۱۲۳ ') = ['NUM3']
tokenizer.tokenize('۱۲۳') = ['۱۲۳']
tokenizer.tokenize(' ۱۱۲۳۲۳۴۲۴۱۲۲۳ ') = ['NUM13']
tokenizer.tokenize('123 -2') = ['123', '-2']
tokenizer.tokenize('123+ 2') = ['123', '+', '2']
tokenizer.tokenize('123*2') = ['123', '*2']
tokenizer.tokenize('0.12') = ['LINK']
tokenizer.tokenize('۰٫۱۲') = ['۰٫۱۲']
tokenizer.tokenize('۱۲٬۰۰۰') = ['۱۲٬', '۰۰۰']
tokenizer.tokenize('0.com') = ['LINK']
tokenizer.tokenize('google.com') = ['LINK']
tokenizer.tokenize('colab.research.google.com') = ['LINK']
tokenizer.tokenize('http://abcd.com') = ['LINK']
tokenizer.tokenize('example.com/index.html?q=hello%20world') = ['LINK', '.', 'html', '?', 'q=hello%20world']
tokenizer.tokenize('abcd@gmail.com') = ['abcd@gLINK']
tokenizer.tokenize('@fardin') = ['@fardin']
tokenizer.tokenize(' @fardin') = ['ID']

برای حل مشکل قرار داشتن توکن در ابتدا یا انتهای رشته من به جای قاعده ([^\w\._]+) از lookahead منفی (?<![\w\._]) استفاده کردم. برای حل مشکل ایمیل هم مکان جایگزین کردن ایمیل را به قبل لینک بردم تا زودتر جایگزین شود. قاعده regex آدرس URL را هم کمی تغییر دادم که کاراکترهای ? . & را هم شامل شود. به دلیل این که در نام دامنه، بخش top-level domain نمی‌تواند شامل یک عدد باشد، من در regex آدرس URL بخش انتهای دامنه را طوری قرار دادم که حاوی عدد نباشد و این گونه مشکل تشخیص لینک به جای عدد حل می‌شود. علاوه بر این کاراکترهای جداساز اعشاری (٫) و جداسار هزارگان (٬) را هم اضافه کردم تا بتوانند جزیی از عدد تشخیص داده شوند.

با ایجاد این تغییرات، برای تست کیس‌های گفته شده در بالا خروجی زیر ایجاد می‌شود:

tokenizer.tokenize('123 ') = ['NUM3']
tokenizer.tokenize(' 123 ') = ['NUM3']
tokenizer.tokenize('123') = ['NUM3']
tokenizer.tokenize(' 12343434343433 ') = ['NUM14']
tokenizer.tokenize('۱۲۳ ') = ['NUM3']
tokenizer.tokenize(' ۱۲۳ ') = ['NUM3']
tokenizer.tokenize('۱۲۳') = ['NUM3']
tokenizer.tokenize(' ۱۱۲۳۲۳۴۲۴۱۲۲۳ ') = ['NUM13']
tokenizer.tokenize('123 -2') = ['NUM3', '-', 'NUM1']
tokenizer.tokenize('123+ 2') = ['NUM3', '+', 'NUM1']
tokenizer.tokenize('123*2') = ['NUM3', '*', 'NUM1']
tokenizer.tokenize('0.12') = ['NUMF']
tokenizer.tokenize('۰٫۱۲') = ['NUMF']
tokenizer.tokenize('۱۲٬۰۰۰') = ['NUMF']
tokenizer.tokenize('0.com') = ['LINK']
tokenizer.tokenize('google.com') = ['LINK']
tokenizer.tokenize('colab.research.google.com') = ['LINK']
tokenizer.tokenize('http://abcd.com') = ['LINK']
tokenizer.tokenize('example.com/index.html?q=hello%20world') = ['LINK']
tokenizer.tokenize('abcd@gmail.com') = ['EMAIL']
tokenizer.tokenize('@fardin') = ['ID']
tokenizer.tokenize(' @fardin') = ['ID']

البته مشکل دیگری که هم‌چنان وجود دارد، تشخیص رشته‌هایی است که درست فاصله‌گذاری نشده‌اند، مانند "است.اما"، که به عنوان لینک شناخته می‌شوند؛ به این دلیل که قاعده \w کاراکترهای الفبای فارسی را هم شامل می‌شود. از طرف دیگر، دامنه‌های جدیدی که دارای پسوند غیر لاتین هستند مانند دامنه‌های ".ایران" هم وجود دارند که برای ان‌ها لازم است از \w استفاده شود. برای همین مطمئن نبودم که باید این قسمت را تغییر دهم یا نه.

از طرف دیگر این قاعده لینک‌های با آدرس آی‌پی مانند 192.168.1.1 و آدرس‌های رزرو شده مانند localhost را هم تشخیص نمی‌دهد؛ اما با توجه به این که این آدرس‌ها به ندرت در متن استفاده می‌شوند و پشتیبانی از آن‌ها regex را پیچیده می‌کند، به نظرم می‌توان از این موارد صرف نظر کرد.

علاوه بر این، تغییرات ایجاد شده را روی چند متن ویکیپدیا امتحان کردم و بجز مورد بالا مشکل خاص دیگری پیدا نکردم.

@nournia
Copy link
Member

nournia commented Aug 11, 2021

سلام
خیلی ممنونم از تغییرات عالی و توضیحات کامل شما.
لطفا آدرس‌ها رو محدود کنید به آدرس‌های انگلیسی که اون مشکل «است.اما» حل بشه. به نظرم این مشکل خیلی شایع هست و دامنه فارسی هم زیاد نداریم.

@bhnum
Copy link
Contributor Author

bhnum commented Aug 11, 2021

ممنون از شما. تغییری رو که گفته بودین اعمال کردم.

@nournia nournia merged commit 171dc3c into roshan-research:master Aug 14, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants