تولید زبان طبیعی در پایتون — راهنمای جامع

۴۵۹ بازدید
آخرین به‌روزرسانی: ۲۱ تیر ۱۴۰۲
زمان مطالعه: ۱۳ دقیقه
تولید زبان طبیعی در پایتون — راهنمای جامع

به احتمال زیاد تا به حال، با اصطلاحاتی نظیر «پردازش زبان طبیعی» (Natural Language Processing)، «تحلیل متن» (Text Analysis) و «متن‌کاوی» (Text Mining) آشنا شده‌اید. تمامی این اصطلاحات برای تعریف دسته‌ای از الگوریتم‌های حوزه «هوش مصنوعی» (Artificial Intelligence) و «یادگیری ماشین» (Machine Learning) استفاده می‌شوند که وظیفه آن‌ها تحلیل، استخراج و مدل‌سازی اطلاعات مفید و بامعنی موجود در «داده‌های متنی غیر ساخت یافته» (Unstructured Text Data) است.

اما شاید تا به حال با مفهوم «تولید زبان طبیعی» (Natural Language Generation) آشنا نشده‌اید. به طور ساده، به فرآیند نرم‌افزاری «تبدیل خودکار» (Automatic Transform) داده‌های خام یا «ساخت یافته» (Structured) به متون حاوی زبان طبیعی، تولید زبان طبیعی گفته می‌شود. اگر بخواهیم درک ملموس‌تری از تولید زبان طبیعی داشته باشیم باشیم، می‌توان این فرایند را متناظر با تبدیل ایده به نوشته یا سخن، توسط انسان‌ها در نظر گرفت. فرایند تولید زبان طبیعی، نقطه مقابل «درک زبان طبیعی» (Natural Language Understanding) به حساب می‌آید.

در فرایند درک زبان طبیعی، سیستم کامپیوتری باید قادر باشد داده‌های متنی ورودی به سیستم را پردازش و «ابهام‌زدایی» (Disambiguate) کند و از این طریق، زبان (یا مجموعه‌ای از دستورالعمل‌های کامپیوتری) لازم برای تولید نمایش ماشینی آن‌ها را تولید کند؛ در حالی که در تولید زبان طبیعی، کامپیوتر یا برنامه کامپیوتری باید قادر باشد داده‌های «ماشین‌خوان» (Machine Readable) را به کلمات یا جملات حاوی زبان طبیعی تبدیل کند.

تفاوت دیگر میان سیستم‌های درک و تولید زبان طبیعی، نامتقارن بودن مسائل قابل حل توسط این دسته از سیستم‌ها است. در درک زبان طبیعی، سیستم باید بر خطا و ابهام موجود در داده‌های حاوی زبانی طبیعی غلبه کند، اما در فرایند تولید زبان طبیعی، ایده‌ها و داده‌هایی که قرار است توسط زبان طبیعی بیان شوند، دقیق و ساخت یافته هستند.نیازمندی اصلی پیاده‌سازی یک سیستم تولید زبان طبیعی، «مالکیت» (Ownership) یا «دسترسی» (Access) به حجم عظیمی از داده‌های خام یا ساخت یافته است. به عبارت دیگر، همیشه پای داده در میان است.

برای اینکه سیستم تولید زبان طبیعی قادر باشد تا روایت حاوی زبان طبیعی از داده‌ها تولید کند، لازم است تا قالب محتوای داده‌ها برای سیستم مشخص شود (از طریق تعریف «قالب» (Template) و یا «جریان‌های کاری» مبتنی بر قاعده (Rule-based Workflow)). سپس داده‌های ساخت یافته و قالب محتوایی آن‌ها به سیستم وارد شده و داده‌های حاوی زبان طبیعی، در خروجی تولید می‌شوند. سیستم‌های تولید زبان طبیعی در وضعیت کنونی، بدون نظارت انسانی، قادر نیستند داده‌های «غیر ساخت یافته» (Unstructured) را به متون نوشته شده به زبان طبیعی تبدیل کنند.

تولید زبان طبیعی (Natural Language Generation)

تولید زبان طبیعی و کاربردهای آن در صنعت

امروزه، بیشترین کاربرد چنین سیستم‌هایی در صنایع و شرکت‌های تجاری است. به ویژه، در حالتی که صنایع و شرکت‌ها نیاز داشته باشند، بدون صرف هزینه و وقت زیاد، داده‌های سازمانی و غیر سازمانی مرتبط را در قالب گزارشات متنی خلاصه‌سازی کنند.

با این حال، دیگر کاربردهای مهم سیستم‌های تولید زبان طبیعی عبارتند از:

  • تولید تحلیل‌ها در قالب نوشتاری جهت نمایش در داشبوردهای «هوش تجاری» (Business Intelligence)
  • تولید خودکار گزارشات متنی منعکس کننده محتویات داده‌های تجاری و تحلیل آن‌ها
  • «شخصی‌سازی» (Personalization) ارتباط با مشتریان از طریق ایمیل و پیام‌‌های درون برنامه‌ای
  • تولید گزارشات مرتبط با وضعیت و شرایط «نگه‌داری» (Maintenance) دستگاه‌های «اینترنت اشیاء» (Internet of Things | IoT)
  • به روز رسانی و خلاصه‌سازی «اسناد مالی» (Financial Portfolio) مشتریان
  • تولید خودکار محتویات متنی مرتبط با توضیحات کالاهای تجاری یا دسته‌بندی آن‌ها در وب‌سایت‌ها یا پلتفرم‌های «تجارت الکترونیک» (E-Commerce)

داده‌های متنی (حاوی زبان طبیعی) تولید شده توسط سیستم‌های تولید زبان طبیعی، باید به گونه‌ای باشند که گویا توسط یک کاربر انسانی نوشته شده‌اند. نگرش خاص موجود در داده‌های متنی تولید شده، محتوا، زمینه موضوعی، شیوه نگارش و ساختارِ روایت خودکار و ماشینی ایجاد شده، بسته به مخاطب نهایی می‌تواند متفاوت باشد. خروجی نهایی چنین سیستم‌هایی توسط مفهومی به نام «طراحی روایت» (Narrative Design) تولید می‌شود.

مدل‌های طراحی روایت، قالب و یا جریان‌های کاری مبتنی بر قاعده هستند که توسط کاربر نهایی یا سازنده‌های سیستم تولید زبان طبیعی تعریف می‌شوند و وظیفه دارند داده‌های ساخت‌یافته را به روایت‌های متنی حاوی زبان طبیعی تبدیل کنند.

تولید زبان طبیعی (Natural Language Generation)

مفاهیم تولید زبان طبیعی

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

پیشرفت‌های اخیر حاصل شده در حوزه «یادگیری عمیق» (Deep Learning) و سیستم‌های مبتنی بر آن (نظیر مدل GPT-2 که توسط شرکت OpenAI توسعه داده شده‌ است)، سبب شده است تا سیستم‌های هوشمندی پدید آیند که قادرند با استفاده از حجم عظیمی از نمونه‌های آموزشی، داده‌های متنی بسیار واقع‌گرایانه‌ای تولید کنند. در این مطلب، برخی از روش‌های مبتنی بر یادگیری ماشین برای تولید زبان طبیعی معرفی می‌شوند و کدهای لازم برای تولید «داده‌های متنی تقلبی» (Fake Text Data) در زبان برنامه‌نویسی پایتون نمایش داده خواهند شد.

مفاهیم و روش‌های ابتدایی در تولید زبان طبیعی

به جای استفاده از روش‌های پیشرفته تولید زبان طبیعی (نظیر یادگیری عمیق)، در این مطلب سعی شده است تا یکی از تکنیک‌های شناخته شده و بسیار سر راست جهت تولید زبان طبیعی معرفی شود. این روش، از یک سو درک مفهوم تولید زبان طبیعی را برای کاربران و مخاطبان این مطلب تسهیل می‌کند و از سوی دیگر، پیاده‌سازی آن (به عنوان نقطه شروعِ مطالعه سیستم‌های تولید زبان طبیعی) در زبان برنامه‌نویسی پایتون ساده و سر راست است. یکی از روش‌‌های معروف و شناخته شده در تولید متن زبان طبیعی، روش «زنجیره‌های مارکوف» (Markov Chains) است. این روش، در عین سادگی پیاده‌سازی، در تولید زبان طبیعی بسیار قدرتمند ظاهر می‌شود.

روش‌های زنجیره مارکوف، فرایندهایی «تصادفی» (Stochastic) هستند که تنها با داشتن «رویداد قبلی» (Previous Event) در دنباله‌ای از رویدادها، قادر به توصیف رویداد بعدی خواهند بود. مزیت چنین مشخصه‌ای در مدل زنجیره‌های مارکوف، در زمان پیش‌بینی رویداد بعدی مشخص می‌شود؛ بدین معنی که سیستم برای پیش‌بینی رویداد یا وضعیت بعدی، لازم نیست از مجموعه تمامی رویدادها یا وضعیت‌های پیشین خود آگاهی داشته باشد. زمانی که از مدل زنجیره‌های مارکوف برای تولید زبان طبیعی استفاده می‌شود، منظور از وضعیت پیشین، کلمه قبلی، 2 کلمه قبلی (Bigram) و یا 3 کلمه قبلی (Trigram) در متن زبان طبیعی تولید شده است.به چنین دنباله‌هایی از کلمات در پردازش زبان طبیعی، N-gram گفته می‌شود.

در مدل‌های تولید زبان طبیعی، از N کلمه قبلی برای تولید (یا پیش‌بینی) کلمه بعدی ممکن در دنباله کلمات استفاده می‌شود. یک مدل زنجیره مارکوف، از طریق روش‌های «وزن‌دهی احتمالی» ( Probabilistic Weighting)، وضعیت بعدی سیستم را مشخص می‌کند. با این حال، اگر در هنگام تولید زبان طبیعی، از روش وزن‌دهی احتمالی برای پیش‌بینی کلمه بعدی در دنباله کلمات استفاده کنیم، روشی حاصل خواهد شد که در آن روش انتخاب کلمات و ساختار کلمات، بسیار «قطعی» (Deterministic) به نظر خواهند رسید. به عبارت دیگر، متون زبان طبیعی تولید شده، بسیار مصنوعی به نظر خواهند آمد.

برای رفع چنین نقیصه‌ای می‌توان تغییراتی در روش وزن‌دهی احتمالی پدید آورد. با این حال، بهترین راه غلبه بر این مشکل، معرفی فاکتورهای «تصادفی» (Random) در هنگام پیش‌بینی وضعیت بعدی سیستم است که سبب تولید جملات زبان طبیعی به مراتب واقع‌گرایانه‎تر و بدیع‌تر می‌شود.

تولید زبان طبیعی (Natural Language Generation)

تولید مدل زبانی

تولید «مدل زبانی» (Language Model) بسیار ساده است. ابتدا نیاز است مجموعه‌ای از داده‌های متنی نمونه (Corpus)، برای تولید مدل زبانی جمع‌آوری شوند. این مجموعه می‌تواند حاوی انواع مختلفی از داده‌های متنی حاوی زبان طبیعی نظیر کتاب، مطالب وب‌سایت‌ها، پست‌های شبکه‌های اجتماعی و سایر موارد باشد.

در این مطلب، تعدادی از سخنرانی‌های منتشر شده از رؤسای جمهور ایالات متحده آمریکا، برای ساختن مدل زبانی مورد استفاده قرار گرفته‌اند. پس از جمع‌آوری داده‌های متنی مورد نیاز برای تولید مدل زبانی، لازم است مراحل زیر دنبال شوند تا سیستم قادر به تولید متون زبان طبیعی باشد. علاوه بر مراحل زیر، توکن (#END#) نیز به مدل زبانی تعریف شده اضافه می‌شود تا «وضعیت پایانی» (Ending State) در هر کدام از متن‌های سخنرانی جمع‌آوری شده مشخص شود.

  • جداسازی واحدهای زبانی موجود در داده‌های متنی جمع‌آوری شده (Tokenization)
  • مرحله اختیاری: انجام هر نوع فرایند پیش‌پردازشی مورد نیاز کاربر (در این مطلب، برای تولید زبان طبیعی از روش‌های پیش‌پردازشی استفاده نشده است.)
  • تولید مدل زبانی N-gram از توکن‌های پردازش شده
  • ایجاد یک «نگاشت» (Mapping) از مدل N-gram به کلمات محتمل بعدی (کلمات بعدی در دنباله، پیش‌‌‌بینی می‌شوند.)

برای اینکه درک بهتری از نحوه تولید مدل زبانی، برای کاربران و مخاطبان این مطلب ایجاد شود، مثال بسیار ساده‌ای در ادامه آورده شده است. فرض کنید که یک داده متنی ساده به شکل (.The dog jumped over the moon. The dog is funny) به سیستم داده شود. با استفاده از فرایند تولید مدل (مراحل بالا)، مدل زبانی و نگاشت‌های N-gram زیر تولید خواهند شد:

(The, dog) -> [jumped, is]
(dog, jumped) -> [over]
(jumped, over) -> [the]
(over, the) -> [moon.]
(dog, is) -> [funny.]
(is, funny) -> [#END#]

به محض اینکه مراحل تولید نگاشت‌های N-gram با موفقیت به پایان رسید، از مدل زبانی تولید شده برای تولید داده‌های متنی جدید استفاده می‌شود. برای تولید داده‌های متنی جدید کافی است تا یک «نقطه آغازین» (Starting Point) برای مدل انتخاب شود. برای انتخاب نقطه آغازین، به دو شیوه می‌توان عمل کرد:

  1. تعریف یک نقطه آغازین جهت تولید داده متنی نظیر عبارت (The dog)، توسط کاربر
  2. انتخاب نقطه آغازین تصادفی، از میان تمامی عبارات N-gram تولید شده

به محض اینکه نقطه آغازین جهت تولید داده متنی برای مدل زبانی تعریف شود، کلمه (وضعیت) بعدی به‌طور تصادفی، از میان لیست کلمات محتمل بعدی انتخاب می‌شود. سپس، عبارت N-gram بعدی، در متن تولید شده مشخص و وضعیت (کلمه) بعدی به‌طور تصادفی و با توجه به عبارت N-gram مشخص شده، از میان لیست کلمات محتمل بعدی انتخاب می‌شود. این رویه، تا پایان فرایند تولید متن حاوی زبان طبیعی ادامه خواهد داشت.

مدل زنجیره مارکوف در پایتون

تا به اینجا با مفهوم مدل زبانی و چگونگی تولید یک مدل زبانی ساده، با استفاده از روش زنجیره مارکوف آشنا شدیم. در ادامه، قطعه کد لازم برای آموزش مدل مارکوف و تولید متن حاوی زبان طبیعی در زبان برنامه‌نویسی پایتون نمایش داده شده است.

1import random
2import string
3
4
5class MarkovModel:
6
7    def __init__(self):
8        self.model = None
9
10    def learn(self,tokens,n=2):
11        model = {}
12
13        for i in range(0,len(tokens)-n):
14            gram = tuple(tokens[i:i+n])
15            token = tokens[i+n]
16
17            if gram in model:
18                model[gram].append(token)
19            else:
20                model[gram] = [token]
21
22        final_gram = tuple(tokens[len(tokens) - n:])
23        if final_gram in model:
24            model[final_gram].append(None)
25        else:
26            model[final_gram] = [None]
27        self.model = model
28        return model
29
30    def generate(self,n=2,seed=None, max_tokens=100):
31        if seed == None:
32            seed = random.choice(self.model.keys())
33
34        output = list(seed)
35        output[0] = output[0].capitalize()
36        current = seed
37
38        for i in range(n, max_tokens):
39            # get next possible set of words from the seed word
40            if current in self.model:
41                possible_transitions = self.model[current]
42                choice = random.choice(possible_transitions)
43                if choice is None: break
44
45                # check if choice is period and if so append to previous element
46                if choice == '.':
47                    output[-1] = output[-1] + choice
48                else:
49                    output.append(choice)
50                current = tuple(output[-n:])
51            else:
52                # should return ending punctuation of some sort
53                if current not in string.punctuation:
54                    output.append('.')
55        return output

در ادامه نگاه دقیق‌تری به کدهای نمایش داده شده و توابع پیاده‌سازی شده در آن خواهیم انداخت. این قطعه کد، از دو تابع اصلی تشکیل شده است؛ یک تابع برای یادگیری مدل مارکوف و تابع دیگر، برای تولید متن زبان طبیعی. تابع یادگیری، مدل مارکوف لازم را برای تولید متن زبان طبیعی، با استفاده از لیستی متشکل از N توکن و عبارت N-gram تولید می‌کند.

1def learn(self,tokens,n=2):
2        model = {}
3
4        for i in range(0,len(tokens)-n):
5            gram = tuple(tokens[i:i+n])
6            token = tokens[i+n]
7
8            if gram in model:
9                model[gram].append(token)
10            else:
11                model[gram] = [token]
12
13        final_gram = tuple(tokens[len(tokens) - n:])
14        if final_gram in model:
15            model[final_gram].append("#END#")
16        else:
17            model[final_gram] = ["#END#"]
18        self.model = model
19        return model

نحوه کار این روش برای تولید زبان طبیعی بدین صورت است که در حلقه ابتدایی تابع، تمامی توکن‌های موجود در داده‌های متنی جمع‌آوری شده پردازش و یک «دایره لغات» (Dictionary) متشکل از تمامی عبارات N-gram (کلمات مجاور یکدیگر در مجموعه داده‌های متنی) ساخته می‌شود. سپس، وقتی که حلقه تابع به عبارت N-gram آخر می‌رسد، عملیات تابع متوقف می‌شود و عبارات N-gram آخر به توکن (#END#) نگاشت می‌شود. توکن (#END#) پایانی برای سیستم تولید زبان طبیعی مشخص می‌کند که به انتهای سند رسیده است؛ یعنی، وضعیت نهایی مدل مارکوف پیشنهادی توسط توکن (#END#) مشخص می‌شود. نمونه‌ای از نگاشت عبارات N-gram آخر به توکن (#END#) در ادامه آمده است.

(be, happy) -> [#END#]

یکی از محدودیت‌های مدل تولید زبان طبیعی پیشنهادی در این مطلب این است که تمامی داده‌‌های موجود در مجموعه داده‌های متنی، با یکدیگر در یک لیست ترکیب می‌شوند؛ در نتیجه، تنها یک وضعیت نهایی (توکن #END#) در مدل مارکوف تعریف می‌شود. یکی از بخش‌های قابل بهبود در مدل تولید زبان طبیعی پیشنهادی، قرار دادن وضعیت نهایی (توکن #END#) در انتهای هر کدام از اسناد پردازش شده در سیستم است. همچنین، می‌توان از وضعیت نهایی (توکن #END#) برای مشخص کردن انتهای جملات در اسناد متنی استفاده کرد. در چنین حالتی، سیستم به شکل بهتری قادر به تولید جملات زبان طبیعی خواهد بود. علاوه بر این، در صورت قرار دادن وضعیت نهایی (توکن #END#) در پایان هر جمله، سیستم تولید زبان طبیعی قادر خواهد بود تا به شکل بهتری، زمان شروع یک جمله جدید را تشخیص دهد. در مرحله بعد، تابع پیاده‌سازی شده برای تولید متن زبان طبیعی مورد بررسی قرار می‌گیرد.

1def generate(self,n=2,seed=None, max_tokens=100):
2        if seed is None:
3            seed = random.choice(list(self.model.keys()))
4
5        output = list(seed)
6        output[0] = output[0].capitalize()
7        current = seed
8
9        for i in range(n, max_tokens):
10            # get next possible set of words from the seed word
11            if current in self.model:
12                possible_transitions = self.model[current]
13                choice = random.choice(possible_transitions)
14                if choice is "#END#" : break
15
16                if choice == '.':
17                    output[-1] = output[-1] + choice
18                else:
19                    output.append(choice)
20                current = tuple(output[-n:])
21            else:
22                # should return ending punctuation of some sort
23                if current not in string.punctuation:
24                    output.append('.')
25        return output

تابع پیاده‌سازی شده برای تولید متن زبان طبیعی، دو آرگومان را به‌عنوان ورودی دریافت می‌کند؛ اندازه عبارت N-gram و اندازه بیشینه برای تعداد توکن‌های موجود در متن زبان طبیعی تولید شده. همچنین پارامتری به نام seed در این تابع وجود دارد. در صورتی که مقدار پارامتر seed برابر با None باشد، سیستم به طور خودکار و کاملا تصادفی، یک عبارت N-gram را از میان تمامی عبارات N-gram ممکن که روی آن‌ها آموزش دیده است، به عنوان نقطه آغازین انتخاب می‌کند. در هر تکرار از این تابع، با توجه به عبارت N-gram قبلی (وضعیت پیشین)، کلمه بعدی (وضعیت بعدی) برای «گذار» (Transition) انتخاب می‌شود. این کار تا زمانی ادامه پیدا می‌کند که در یکی از تکرارها، مدل به وضعیت نهایی (توکن #END#) برسد و یا شرط اندازه بیشینه برای تعداد توکن‌های موجود در متن تولید شده ارضا شود.

در ادامه، یک نمونه متن زبان طبیعی تولید شده با استفاده از توابع بالا نمایش داده می‌شود. در این مثال، مدل زنجیره مارکوف، روی مدل زبانی Bigram آموزش دیده است.

Us from carrying out even the dishonest media report the facts! my hit was on the 1st of december, 1847, being the great reviews & will win on the front lines of freedom. we are now saying you will never forget the rigged system that is what we do at a 15 year high. i can perceive no good reason why the civil and religious freedom we enjoy and by the secretary of war would be 0.0 ratings if not.

همان طور که مشاهده می‌شود، متن تولید شده ساختار تقریبا مناسبی دارد ولی تصادفی بود آن کاملا مشهود است. در ادامه، یک نمونه دیگر از متن زبان طبیعی تولید شده نمایش داده شده است. در این مثال، مدل زنجیره مارکوف، روی مدل زبانی Trigram آموزش دیده است.

Was $7,842,306.90, and during the seven months under the act of the 3d of march last i caused an order to be issued to our military occupation during the war, and may have calculated to gain much by protracting it, and, indeed, that we might ultimately abandon it altogether without insisting on any indemnity, territorial or otherwise. whatever may be the least trusted name in news if they continue to cover me inaccurately and with a group, it’s going to be open, and the land will be left in better shape than it is right now. is that right? better shape. (applause.) we declined to certify the terrible one-sided iran nuclear deal. that was a horrible deal. (applause.) whoever heard you give $150 billion to a nation

متن دوم، ساختار بهتری نسبت به متن اول دارد ولی با بررسی دقیق‌تر جملات تولید شده می‌توان دریافت که مدل آموزش دیده، جملات و عبارات موجود در داده‌های متنی جمع‌آوری شده را به‌طور تصادفی انتخاب و در کنار یکدیگر قرار می‌دهد. در واقعیت، تا زمانی که حجم عظیمی از داده‌ها برای آموزش مدل زنجیره مارکوف در اختیار نباشد، به محض استفاده از مدل‌های زبانی Trigram و بالاتر، شاهد بروز چنین رفتاری در سیستم تولید زبان طبیعی خواهیم بود. این نقیصه، یکی از بزرگترین مشکلات مدل زنجیره مارکوف است.

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

مدل زنجیره مارکوف توزیع‌ شده با استفاده از بسته Spark

فرض کنید به جای مجموعه‌ای از سخنرانی‌های منتشر شده از رؤسای جمهور ایالات متحده آمریکا، مجموعه‌ای متشکل از پیام‌های آرشیو شده در شبکه‌های اجتماعی در دسترس باشند. کدهای ارائه شده برای مدل‌سازی و تولید زبان طبیعی از روی داده‌های متنی، در صورتی که حجم حافظه‌ای و توان پردازشی کافی برای تحلیل آن‌ها موجود باشد، به احتمال زیاد قادر به پردازش این حجم از داده خواهد بود ولی «مقیاس‌پذیری» (Scalability) به شدت ضعیفی خواهند داشت. در چنین حالتی، بهتر است که از بسته نرم‌افزاری Apache Spark و قابلیت‌های محاسبات توزیع‌شده آن، جهت ساخت و ذخیره مدل زنجیره مارکوف استفاده شود. در ادامه، کدهای پیاده‌سازی مدل زنجیره مارکوف با استفاده از Apache Spark توضیح و نمایش داده شده است.

بزرگترین مشکل پیاده‌سازی مدل زنجیره مارکوف با استفاده از Apache Spark، چگونگی تولید مدل N-gram، تولید ساختاری شبیه به دایره لغات برای ذخیره عبارات N-gram و پرس و جو در آن‌ها است. خوشبختانه، بسته Apache Spark قابلیتی برای استخراج ویژگی‌های N-gram از اسناد متنی دارد. این قابلیت در بسته Apache Spark، یک شیء از «فریم داده‌ای» (Dataframe) بسته Spark و اسناد متنی پردازش شده (Tokenized) را به عنوان آرگومان ورودی دریافت می‌کند و عبارات N-gram موجود در داده‌های متنی را، به عنوان خروجی تولید می‌کند.

1ngram = NGram(n=self.n, inputCol='tokenized_text',outputCol='ngram')
2ngram_df = ngram.transform(text_df)

با استفاده از بسته Apache Spark، می‌توان تابعی برای تولید نگاشت (Mapping) از مدل N-gram به کلمات (مجاور) محتمل بعدی ایجاد کرد. هدف این تابع این است که به ازاء هر کدام از اسناد پردازش شده، لیستی از «چندتایی‌ها» (Tuples) به شکل [(ngram, adjacent term)] تولید کند.

1def generate_adjacent_terms(ngrams):
2    adjacent_list = []
3    for i in range(0, len(ngrams)):
4        if(i == len(ngrams) - 1):
5            adjacent_tuple = (ngrams[i], "#END#")
6            adjacent_list.append(adjacent_tuple)
7        else:
8            adjacent_tuple = (ngrams[i], ngrams[i+1].split(" ")[-1])
9            adjacent_list.append(adjacent_tuple)
10    return adjacent_list

در نهایت، عبارات N-gram در اسناد مختلف با یکدیگر ترکیب می‌شوند و دوتایی‌های جدید به شکل  [(ngram, adjacent term list)] تولید می‌شوند. نکته مهمی که در این بخش وجود دارد این است که کلمات تکراری زیادی در لیست‌ «کلمات مجاور» (Adjacent Term) هر عبارات N-gram ظاهر خواهند شد. از کلمات تکراری موجود در لیست، به عنوان «وزن» (Weight) کلمات در هنگام انتخاب کلمه بعدی (وضعیت بعدی) استفاده می‌شود.

1self.ngram_model = ngram_df.rdd.map(lambda x: PreProcess
2    .generate_adjacent_terms(x.asDict()['ngram'])) \
3    .flatMap(lambda xs: [x for x in xs]) \
4    .map(lambda y: (y[0], [y[1]])) \
5    .reduceByKey(lambda a, b: a + b)

در ادامه، کدهای پیاده‌سازی مدل زنجیره مارکوف با استفاده از Apache Spark نمایش داده شده است.

1from pyspark.ml.feature import NGram
2
3import PreProcess
4import random
5
6
7class MarkovModelSpark:
8
9    def __init__(self, spark_session, n=2):
10        self.spark_session = spark_session
11        self.ngram_model = None
12        self.model_keys = None
13        self.n = n
14
15    def learn(self, text_df):
16        """Spark transformation to learn the adjacent terms of a given ngram"""
17
18        ngram = NGram(n=self.n, inputCol='tokenized_text', outputCol='ngram')
19        ngram_df = ngram.transform(text_df)
20        # create the ngram to adjacent term mappings
21        self.ngram_model = ngram_df.rdd \
22            .map(lambda x: PreProcess.generate_adjacent_terms(x.asDict()['ngram'])) \
23            .flatMap(lambda xs: [x for x in xs]) \
24            .map(lambda y: (y[0], [y[1]])) \
25            .reduceByKey(lambda a, b: a + b)
26
27        # create list of the keys in the model and store them
28        self.model_keys = self.ngram_model.map(lambda x: x[0]).collect()
29
30    def retrain(self, text_df):
31        """Function to retrain a model given new text input so the model does not have to be fully retrained on all
32        the data but only the parts that are new. Assumes the text_df is in the same format as the already trained
33        model."""
34
35        raise NotImplementedError("Retrain functionality has not been implemented yet.")
36
37    def generate(self, seed=None, end_token_stop=True, max_tokens=125):
38        """Generate text based on the model learned on the corpus"""
39
40        if self.ngram_model is None:
41            raise ValueError('Model has not been generated yet. Run the learn function first.')
42
43        if seed is None:
44            seed = random.choice(self.model_keys)
45
46        output = seed.split(" ")
47        output[0] = output[0].capitalize()
48        current = seed
49
50        for i in range(0, max_tokens):
51            if current in self.model_keys:
52                next_token = self.__get_next_token(current)
53                if next_token is None or next_token == '#END#':
54                    if end_token_stop:
55                        break
56                    else:
57                        current = random.choice(self.model_keys)
58                        next_token = self.__get_next_token(current)
59                output.append(next_token)
60                current = " ".join(output[-self.n:])
61
62        return " ".join(output)
63
64    def __get_next_token(self, current):
65        return random.choice(self.ngram_model.lookup(current)[0])
66
67    def save_model(self, file_path, spark_pickle=True):
68        if spark_pickle:
69            self.ngram_model.saveAsPickleFile(file_path)
70        else:
71            raise NotImplementedError("Only support for Spark based pickling currently implemented.")
72
73    def load_model(self, file_path, spark_pickle=True):
74        if spark_pickle:
75            self.ngram_model = self.spark_session.sparkContext.pickleFile(file_path)
76        else:
77            raise NotImplementedError("Only support for Spark based pickling currently implemented.")
78            

کدهای پیاده‌سازی مدل زنجیره مارکوف با استفاده از Apache Spark، خروجی مشابه با کدهای مرحله قبل تولید می‌کنند؛ با این تفاوت که مقیاس‌پذیری بسیار بهتری نسبت به کدهای مرحله قبل دارند.

اگر نوشته بالا برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

^^

بر اساس رای ۶ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
Towards Data Science
۱ دیدگاه برای «تولید زبان طبیعی در پایتون — راهنمای جامع»

درود بر شما. جای چنین مطالب آموزشی در کشور بسیار خالیست.

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *