مدل های Seq2Seq برای خلاصه سازی متن با استفاده از کراس و تنسورفلو — راهنمای جامع

۳۵۷ بازدید
آخرین به‌روزرسانی: ۲۱ شهریور ۱۴۰۲
زمان مطالعه: ۱۴ دقیقه
مدل های Seq2Seq برای خلاصه سازی متن با استفاده از کراس و تنسورفلو — راهنمای جامع

در این راهنما روش خلاصه کردن متن و ایجاد ویژگی‌هایی از بخش issue های گیت‌هاب را با استفاده از یادگیری عمیق به کمک Keras و TensorFlow بررسی می‌کنیم. در تصویر زیر می‌توانید پیش‌بینی‌هایی که توسط سیستم برای متن مفروض صورت گرفته است را درون کادرها مشاهده کنید.

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

سرآغاز: انگیزه نگارش

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

با این حال، نمونه‌های قابل بازتولیدی از تکنیک‌های یادگیری ماشین که عملاً در صنایع استفاده شوند، زیاد نیستند. در این نوشته شما با یک نمونه «کمینه محصول پذیرفتنی» (minimally viable product) آشنا می‌شوید که شیوه استفاده از یادگیری عمیق برای ایجاد محصولات داده‌ای از متن‌ها (یعنی issue های گیت‌هاب) را نشان می‌دهد.

در این راهنما بر روی استفاده از مدل‌های جمله به جمله برای خلاصه‌سازی متن در issue های گیت‌هاب تمرکز می‌کنیم و موارد زیر را ثابت می‌کنیم:

  • لازم نیست توان پردازشی زیادی برای دسترسی به نتایج ملموس داشته باشید (ما در این نوشته از یک GPU منفرد استفاده می‌کنیم)
  • لازم نیست کد زیادی بنویسید. تماشای این که چگونه چند خط معدود از کد می‌تواند چیزی چنین جادویی تولید کند، واقعاً شگفت‌انگیز است.
  • حتی اگر نمی‌خواهید متون را خلاصه‌سازی کنید، آموزش دادن یک مدل برای اجرای این وظیفه برای تولید ویژگی‌هایی برای وظایف دیگر کاملاً مفید است.

مواردی که در لابه لای مطالب این راهنما ارائه می‌کنیم:

  • چگونگی گردآوری داده‌ها و آماده‌سازی آن‌ها برای یادگیری عمیق
  • چگونگی ساخت معماری مدل seq2seq و آموزش دادن مدل.
  • چگونگی آماده‌سازی مدل برای استنتاج، بررسی و نمایش موارد استفاده مختلف.

هدف ما تمرکز روی ارائه یک نمونه صفر تا صد است تا بتوانید مدل مفهومی گردش کار را توسعه دهید و نه این که صرفاً به بررسی جنبه‌های ریاضیاتی مدل بپردازیم. در این مسیر لینک‌هایی ارائه می‌شوند که در صورت نیاز به مطالعه بیشتر می‌توانید از آن‌ها استفاده کنید.

گردآوری داده‌ها

اگر با Issue های گیت‌هاب آشنا نیستید، قویاً توصیه می‌شود که پیش از مطالعه این راهنما این بخش از گیت‌هاب را بررسی کنید. به طور خاص بخش‌هایی از داده‌ها که در این تمرین استفاده می‌شوند، متن و عنوان‌های Issue گیت‌هاب هستند. نمونه‌ای از آن به صورت زیر است:

ما جفت (متن Issue- عنوان Issue) زیادی با هدف آموزش مدلمان برای خلاصه‌سازی آن‌ها گردآوری خواهیم کرد. ایده کار این است که مثال‌های زیادی از توضیح Issue و عناوین آن‌ها را ببینیم تا بتوانیم روش خلاصه‌سازی Issue های جدید را بیاموزیم.

اگر از گیت‌هاب استفاده نمی‌کنید، بهترین روش برای کسب داده‌های گیت‌هاب استفاده از این پروژه شگفت‌انگیز متن-باز است که به صورت زیر توصیف شده است:

پروژه‌ای که تایملاین گیت‌هاب عمومی را ثبت کرده، آن را آرشیو می‌کند و برای تحلیل بیشتر آماده می‌سازد.

ما قبلاً داده‌ها را با استفاده از این ابزار گردآوری کرده‌ایم و شما می‌توانید آن را از این لینک دانلود کنید.

آماده‌سازی و پاکسازی داده‌ها

اینک که داده‌هایمان را در اختیار داریم باید آن‌ها را برای مدلسازی آماده کنیم. پیش از ورود به بخش کدنویسی ابتدا دو نمونه از این اسناد را به صورت مقدماتی بررسی می‌کنیم.

[“The quick brown fox jumped over the lazy dog 42 times.”, “The dog is lazy”]

در ادامه چشم‌انداز تقریبی از مراحلی که به ترتیب برای پیش‌پردازش این متن خام لازم است را می‌بینید:

1. پاکسازی متن

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

[“the quick brown fox jumped over the lazy dog *number* times”, “the dog is lazy”]

2. توکن‌دار کردن

هر سند به فهرستی از واژه‌ها افراز می‌شود:

[[‘the’, ‘quick’, ‘brown’, ‘fox’, ‘jumped’, ‘over’, ‘the’, ‘lazy’, ‘dog’, ‘*number*’, ‘times’], [‘the’, ‘dog’, ‘is’, ‘lazy’]]

3. ساخت واژه‌نامه

شما باید هر کلمه متمایز را به صورت یک عدد صحیح ثبت کنید. این بدان معنی است که باید یک نگاشت به صورت token -> integers بسازید. به علاوه، توصیه می‌شود که یک عدد صحیح مانند 0 را برای واژه‌های نادری که از آستانه کمتری در متن ظاهر می‌شوند حفظ کنید. در ادامه این مورد را بیشتر توضیح داده‌ایم. پس از اعمال نگاشت token -> integers داده‌های شما به صورت زیر می‌آید:

[[2, 3, 4, 5, 6, 7, 2, 8, 9, 10, 11], [2, 9, 12, 8]]

4. فاصله‌گذاری (padding)

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

[[2, 3, 4, 5, 6, 7, 2, 8, 9, 10, 11], [0, 0, 0, 0, 0, 0, 0, 2, 9, 12, 8]]

یک روش معقول برای تعیین طول سندها این است که هیستوگرامی از طول سندها بسازید تا بتوانید عدد معقولی را انتخاب کنید. توجه داشته باشید که در مثال فوق داده‌ها در ابتدا با 0 پر شدند؛ اما می‌توانستیم از آخر هم پر کنیم. این مسئله در بخش بعد بیشتر بررسی خواهد شد.

آماده‌سازی داده‌های Issue های گیت‌هاب

به همین دلیل بهتر است از این راهنما پیروی کنید. داده‌هایی که با آن‌ها کار می‌کنیم چنین وضعیتی دارند:

دیتافریم Pandas با متن و عناوین Issue های گیت‌هاب

می‌بینیم که عنوان و متن Issue ها وجود دارند که به طور مستقل از هم پردازش خواهند شد. ما از URL ها در بحث مدلسازی استفاده نمی‌کنیم و صرفاً جهت ارجاع ذخیره شده‌اند. توجه کنید که ما 2 میلیون Issue نمونه‌برداری شده از 5 میلیون Issue اولیه را انتخاب کردیم تا این آموزش برای افراد دیگر نیز قابل پیگیری باشد.

برخی افراد روش پیش‌پردازش داده‌ها برای یادگیری عمیق را فرایند بسیار تکراری تلقی می‌کنند. Keras ابزارهای مناسبی برای کمک به این فرایند دارد؛ با این حال ما می‌خواهیم این وظایف را برای افزایش سرعت به صوت موازی اجرا کنیم.

بسته ktext

برنامه‌ای به نام ktext وجود دارد که به اجرای این مراحل پیش‌پردازش که در بخش‌های قبل معرفی کردیم، کمک می‌کند. این کتابخانه لایه پوششی نازکی پیرامون ابزارهای پردازش متن keras و spacy محسوب می‌شود و از تردبندی (threading) مبتنی بر پردازش پایتون برای افزایش سرعت پردازش بهره می‌گیرد. ضمناً این برنامه همه مراحل پیش‌پردازش را با هم به صوت زنجیری اجرا می‌کند و موجب سادگی کار می‌شود. دقت کنید که این بسته در حال توسعه است و اگر از آن استفاده می‌کنید در این خصوص هوشیار باشید. برای این که ببینید این کتابخانه چگونه کار می‌کند به این راهنما نگاه کنید. البته توصیه می‌کنیم فعلاً به ادامه مطالعه این راهنما بپردازید.

برای پردازش داده‌های متن Issue ها از کد زیر استفاده می‌کنیم.

from ktext.preprocess import processor
# instantiate data processing object
body_pp = processor(keep_n=8000, padding_maxlen=70)
# process data
train_body_vecs = body_pp.fit_transform(train_body_raw)

کد فوق داده‌های متنی ما را پاکسازی و توکن‌دار می‌کند و از فاصله گذار ابتدایی و قطع کردن انتهایی به طرزی استفاده می‌کند که طول سند 70 کلمه طول داشته باشد. این طول بر اساس بررسی هیستوگرام‌ها و طول سندها که از سوی ktext ارائه شده بود انتخاب شده است. به علاوه تنها 8000 کلمه در واژه‌نامه باقی مانده است و بقیه واژه‌ها که نادر بوده‌اند دارای اندیس 1 هستند. این اندیس اختیاری است. این فرایند پردازش روی یک وهله از سرورهای AWS p3.2xlarge با 8 هسته پردازشی و 60 گیگابایت حافظه در حدود یک ساعت طول کشیده است. در ادامه نمونه‌ای از داده‌های خام در برابر داده‌های پردازش شده را می‌بینید.

عنوان‌ها نیز تقریباً به همین روش پردازش می‌شوند، تنها چند تفاوت کوچک وجود دارد.

# instantiate the pre-processor for titles
title_pp = processor(append_indicators=True, keep_n=4500, 
                     padding_maxlen=12, padding ='post')
# process the titles
train_title_vecs = title_pp.fit_transform(train_title_raw)

این بار ما برخی پارامترهای اضافی ارسال می‌کنیم:

  • =ppend_indicators - باعث می‌شود که توکن‌های ‘_start_’ و ‘_end_’ به ترتیب به ابتدا و انتهای هر سند اضافه شود.
  • 'padding=’post – به این معنی است که از فاصله‌گذاری 0 در انتهای متن به جای ابتدا که پیش‌فرض است استفاده می‌شود.

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

تعریف معماری مدل

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

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

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

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

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

این شبکه که برای این مسئله استفاده می‌شود کاملاً مشابه راهکاری است که در راهنمای فوق توصیف شده بود و کد آن به این شکل است:

from keras.models import Model
from keras.layers import Input, LSTM, GRU, Dense, Embedding, Bidirectional, BatchNormalization
from keras import optimizers

#arbitrarly set latent dimension for embedding and hidden units
latent_dim = 300

##### Define Model Architecture ######

########################
#### Encoder Model ####
encoder_inputs = Input(shape=(doc_length,), name='Encoder-Input')

# Word embeding for encoder (ex: Issue Body)
x = Embedding(num_encoder_tokens, 
              latent_dim, 
              name='Body-Word-Embedding', 
              mask_zero=False)(encoder_inputs)

x = BatchNormalization(name='Encoder-Batchnorm-1')(x)

# We do not need the `encoder_output` just the hidden state.
_, state_h = GRU(latent_dim, return_state=True, name='Encoder-Last-GRU')(x)

# Encapsulate the encoder as a separate entity so we can just 
#  encode without decoding if we want to.
encoder_model = Model(inputs=encoder_inputs, 
                      outputs=state_h, 
                      name='Encoder-Model')

seq2seq_encoder_out = encoder_model(encoder_inputs)

########################
#### Decoder Model ####
decoder_inputs = Input(shape=(None,), name='Decoder-Input')  # for teacher forcing

# Word Embedding For Decoder (ex: Issue Titles)
dec_emb = Embedding(num_decoder_tokens, 
                    latent_dim, 
                    name='Decoder-Word-Embedding', 
                    mask_zero=False)(decoder_inputs)

dec_bn = BatchNormalization(name='Decoder-Batchnorm-1')(dec_emb)

# Set up the decoder, using `decoder_state_input` as initial state.
decoder_gru = GRU(latent_dim, 
                  return_state=True, 
                  return_sequences=True, 
                  name='Decoder-GRU')

decoder_gru_output, _ = decoder_gru(dec_bn, initial_state=seq2seq_encoder_out)
x = BatchNormalization(name='Decoder-Batchnorm-2')(decoder_gru_output)

# Dense layer for prediction
decoder_dense = Dense(num_decoder_tokens, 
                      activation='softmax', 
                      name='Final-Output-Dense')

decoder_outputs = decoder_dense(x)

########################
#### Seq2Seq Model ####

#seq2seq_decoder_out = decoder_model([decoder_inputs, seq2seq_encoder_out])
seq2seq_Model = Model([encoder_inputs, decoder_inputs], decoder_outputs)


seq2seq_Model.compile(optimizer=optimizers.Nadam(lr=0.001), 
loss='sparse_categorical_crossentropy')

وقتی کد فوق را مطالعه می‌کنید، متوجه می‌شوید که ارجاع‌هایی به مفهوم «teacher-forcing» دارد. teacher forcing یک مکانیسم بسیار مهم است که امکان آموزش سریع شبکه را فراهم می‌سازد. این مسئله در این نوشته بهتر توصیف شده است.

ممکن است تعجب کنید که از کجا به معماری فوق رسیده‌ایم. این کار با شروع از نمونه‌های عمومی و اجرای آزمایش‌های بسیار به دست آمده است. تصویر کمیک فوق این وضعیت را بهتر توضیح می‌دهد. شاید متوجه شده باشید که تابع زیان ما به جای categorical crossentropy به صورت sparse categorical crossentropy است. دلیل این مسئله آن است که این وضعیت اجازه می‌دهد که از اعداد صحیح برای پیش‌بینی اهداف به جای انکودینگ one-hot برای اهداف خود استفاده کنیم که از نظر حافظه بسیار کارآمدتر است.

آموزش دادن مدل

کدی که برای آموزش دادن مدل استفاده می‌شود، تقریباً سرراست است و شامل فراخوانی متد برازش روی شیء مدل تعریف شده است. ما پارامترهای اضافی مانند callback ها برای گزارش‌گیری، تعداد تکرارها و اندازه دسته را نیز به این متد ارسال می‌کنیم.

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

from keras.callbacks import CSVLogger, ModelCheckpoint

#setup callbacks for model logging
script_name_base = 'tutorial_seq2seq'
csv_logger = CSVLogger('{:}.log'.format(script_name_base))
model_checkpoint = ModelCheckpoint('{:}.epoch{{epoch:02d}}-val{{val_loss:.5f}}.hdf5'.format(script_name_base),
                                   save_best_only=True)

# pass arguments to model.fit
batch_size = 1200
epochs = 7
history = seq2seq_Model.fit([encoder_input_data, decoder_input_data], np.expand_dims(decoder_target_data, -1),
          batch_size=batch_size,
          epochs=epochs,
validation_split=0.12, callbacks=[csv_logger, model_checkpoint])
Train on 1584000 samples, validate on 216000 samples
Epoch 1/7
1584000/1584000 [================] - 411s 259us/step - 
loss: 2.6989 - val_loss: 2.3833
Epoch 2/7
1584000/1584000 [================] - 265s 167us/step - 
loss: 2.2941 - val_loss: 2.3035
Epoch 3/7
1584000/1584000 [================] - 264s 167us/step - 
loss: 2.2085 - val_loss: 2.2740
Epoch 4/7
1584000/1584000 [================] - 265s 167us/step - 
loss: 2.1583 - val_loss: 2.2611
Epoch 5/7
1584000/1584000 [================] - 267s 168us/step - 
loss: 2.1226 - val_loss: 2.2555
Epoch 6/7
1584000/1584000 [================] - 265s 167us/step - 
loss: 2.0947 - val_loss: 2.2521
Epoch 7/7
1584000/1584000 [================] - 264s 167us/step - 
loss: 2.0718 - val_loss: 2.2563

ما این مدل را روی یک وهله از سرورهای AWS p3.2xlarge تمرین می‌دهیم که برای 7 تکرار تقریباً 35 دقیقه طول می‌کشد. در یک سناریوی عملی احتمالاً چنین مدلی باید به مدتی طولانی‌تر آموزش ببیند و از callback های اضافی برای توقف سریع و اصلاح نرخ یادگیری به صورت دینامیک استفاده شود. با این حال، ما دریافتیم که رویه تمرینی مطرح شده فوق برای یک پروژه به صورت «کمینه محصول پذیرفتنی» مناسب است.

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

آماده‌سازی مدل برای استنتاج

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

اگر تصویر فوق برایتان گویا نیست، می‌توانید از این راهنما استفاده کنید. دکودر با استفاده از کد زیر که به خوبی کامنت شده است، مجدداً مونتاژ شده است:

def extract_decoder_model(model):
    """
    Extract the decoder from the original model.
    Inputs:
    ------
    model: keras model object
    Returns:
    -------
    A Keras model object with the following inputs and outputs:
    Inputs of Keras Model That Is Returned:
    1: the embedding index for the last predicted word or the <Start> indicator
    2: the last hidden state, or in the case of the first word the hidden state 
       from the encoder
    Outputs of Keras Model That Is Returned:
    1.  Prediction (class probabilities) for the next word
    2.  The hidden state of the decoder, to be fed back into the decoder at the 
        next time step
    Implementation Notes:
    ----------------------
    Must extract relevant layers and reconstruct part of the computation graph
    to allow for different inputs as we are not going to use teacher forcing at
    inference time.
    """
    # the latent dimension is the same throughout the architecture so we are going to
    # cheat and grab the latent dimension of the embedding because that is the same as 
    # what is output from the decoder
    latent_dim = model.get_layer('Decoder-Word-Embedding').output_shape[-1]

    # Reconstruct the input into the decoder
    decoder_inputs = model.get_layer('Decoder-Input').input
    dec_emb = model.get_layer('Decoder-Word-Embedding')(decoder_inputs)
    dec_bn = model.get_layer('Decoder-Batchnorm-1')(dec_emb)

    # Instead of setting the intial state from the encoder and forgetting about it, 
    # during inference we are not doing teacher forcing, so we will have to have a 
    # feedback loop from predictions back into the GRU, thus we define this input 
    # layer for the state so we can add this capability
    gru_inference_state_input = Input(shape=(latent_dim,), name='hidden_state_input')

    # we need to reuse the weights that is why we are getting this
    # If you inspect the decoder GRU that we created for training, it will take as 
    # input 2 tensors -> (1) is the embedding layer output for the teacher forcing,
    #                     which will now be the last step's prediction, and will be 
    #                      _start_ on the first time step.
    #                    (2) is the state, which we will initialize with the encoder 
    #                    on the first time step, but then grab the state after the 
    #                    first prediction and feed that back in again.
    gru_out, gru_state_out = model.get_layer('Decoder-GRU')([dec_bn, 
                                                             gru_inference_state_input])

    # Reconstruct dense layers
    dec_bn2 = model.get_layer('Decoder-Batchnorm-2')(gru_out)
    dense_out = model.get_layer('Final-Output-Dense')(dec_bn2)
    decoder_model = Model([decoder_inputs, gru_inference_state_input],
                          [dense_out, gru_state_out])
return decoder_model

تابع‌های کمکی زیادی استفاده شده‌اند تا پیش‌بینی‌هایی که در این فایل قرار دارند ایجاد شوند. به طور خاص متد generate_issue_title سازوکار پیش‌بینی عنوان‌ها را نشان می‌دهد. پیشنهاد می‌کنیم که مطالعه دقیق کد فوق برای درک بهتر طرز کار پیش‌بینی‌ها بسیار مفید خواهد بود.

نمایش آنچه این مدل می‌تواند انجام دهد

1. خلاصه‌سازی متن و ایجاد دموی آماده استفاده کاملاً مناسب

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

توانایی خلاصه‌سازی متن، خود می‌تواند یک محصول داده‌ای مفید باشد. برای نمونه پیشنهاد خودکار عنوان به کاربران را تصور کنید. با این حال این احتمالاً مفیدترین بخش این مدل نیست. قابلیت‌های دیگر این مدل در بخش بعدی توضیح داده شده‌اند.

نمونه‌ای از خلاصه‌سازی متن روی یک مجموعه مورد بررسی در ادامه ارائه شده است:

2. استخراج ویژگی‌هایی که می‌تواند برای وظایف مختلف مفید باشد

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

در این راهنما، انکودر یک بردار 300 بعدی برای هر issue ایجاد می‌کند. این بردار می‌تواند به منظور اجرای وظایف مختلف یادگیری ماشین استفاده شود:

  • ساخت یک سیستم توصیه گر برای یافتن issue های تکراری
  • تشخیص issue هایی که اسپم هستند.
  • ارائه ویژگی‌های اضافی به مدل رگرسیون که مقدار زمان باز بودن یک issue را تشخیص دهد.
  • ارائه ویژگی‌های اضافی به classifier تا تشخیص دهد کدام issue ها شامل باگ یا آسیب‌پذیری هستند.

لازم به ذکر است که روش‌های مختلفی برای استخراج ویژگی‌ها از متن وجود دارد و تضمینی وجود ندارد که این ویژگی‌ها برای یک وظیفه خاص در مقایسه با متد دیگر برتری داشته باشند. ما دریافتیم که در اغلب موارد ترکیب ویژگی‌های استخراج شده از این رویکرد با ویژگی‌های دیگر مفیدتر خواهد بود. با این حال نکته اصلی که باید اشاره کنیم این است که شما این ویژگی‌ها را به صورت رایگان به عنوان اثر جانبی آموزش دادن مدل برای خلاصه‌سازی متن به دست می‌آورید!

در ادامه مثالی از این مفهوم به صوت عملی برای توصیه issue ها مشابه ارائه شده است. به این دلیل که انکودر یک بردار 300 بُعدی ارائه می‌کند که هر issue را توصیف می‌کند، بهتر است نزدیک‌ترین همسایگی برای هر issue را در فضای بردار بیابیم. با استفاده از بسته annoy نزدیک‌ترین همسایگی به علاوه تولید یک عنوان issue برای چند issue ارائه شده است:

پیش‌بینی‌ها در کادر مستطیلی هستند. نت‌بوک در این لینک موجود است.

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

نکته هیجان‌انگیزتر این است که این تکنیک صرفاً محدود به issue ها نیست؛ بلکه ما می‌توانیم از این رویکرد برای تولید عنوان‌های repo از فایل‌های ReadMe یا بخش کامنت ها و docstring کد نیز استفاده کنیم. این قابلیت‌ها بی‌نهایت هستند.

ارزیابی مدل

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

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

مراحل بعدی

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

  • افزودن لایه‌های attention به همراه RNN های دوطرفه.
  • پشته سازی لایه‌های تکراری بیشتر در بخش‌های انکودر و دیکودر و تغییر دادن اندازه لایه‌های مختلف
  • استفاده از رگولاریزاسیون (مانند Dropout)
  • پیش آموزش همه واژه‌ها در کل بخش issue ها
  • استفاده از یک توکنایزر بهتر که بتواند متن‌های آمیخته با کد را مدیریت کند و قالب‌های issue و دیگر بخش‌های نشانه گذاری شده را تشخیص دهد.
  • آموزش دادن داده‌های بیشتر (ما در این مقاله تنها 2 میلیون issue را بررسی کردیم؛ اما داده‌های بیشتری وجود دارند.
  • برای پیش‌بینی عنوان‌های issue ها به جای رویکرد حریصانه واژه بعدی از راهکار beam search استفاده کنید.
  • کتابخانه fastai را که بر مبنای pytorch ساخته شده است بررسی کنید. این کتابخانه چند تکنیک تثبیت شده برای NLP دارد. این کار نیازمند سوئیچ کردن از Keras به PyTorch است.

برخی از موارد فوق موضوعات پیشرفته‌تری محسوب می‌شوند؛ اما یادگیری آن‌ها چندان دشوار نیست.

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

==

بر اساس رای ۲ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
towardsdatascience
نظر شما چیست؟

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