خلاصه سازی متن

/*! elementor - v3.17.0 - 08-11-2023 */ .elementor-heading-title{padding:0;margin:0;line-height:1}.elementor-widget-heading .elementor-heading-title[class*=elementor-size-]>a{color:inherit;font-size:inherit;line-height:inherit}.elementor-widget-heading .elementor-heading-title.elementor-size-small{font-size:15px}.elementor-widget-heading .elementor-heading-title.elementor-size-medium{font-size:19px}.elementor-widget-heading .elementor-heading-title.elementor-size-large{font-size:29px}.elementor-widget-heading .elementor-heading-title.elementor-size-xl{font-size:39px}.elementor-widget-heading .elementor-heading-title.elementor-size-xxl{font-size:59px}
text summarizer (خلاصه سلزی متن با transformer)
تعریف خلاصه سازی متن: گرفتن یک متن بلند و دادن یک متن با تعداد کلمات کمتر ولی هم مفهوم با متن قبلی
ابتدا با انواع خلاصه سازی آشنا شویم
- 1 - Extractive Summarization
- 2 - Abstractive Summarization
در Extractive summarization به اینگونه هست که یک سری جمله از جملات انتخاب میکنیم میکنیم و روی آنها مدل را ران میکنیم:
/*! elementor - v3.17.0 - 08-11-2023 */ .elementor-widget-image{text-align:center}.elementor-widget-image a{display:inline-block}.elementor-widget-image a img[src$=".svg"]{width:48px}.elementor-widget-image img{vertical-align:middle;display:inline-block}
در حالت abstractive summarization به اینگونه هست که متن را میگیرد و یک متن جدید میسازد که این حالت را با دیتا ست cnn text summary آن را انجام میدهیم

برای انجام این کار ما نیاز به یک معماری به نام teacher forcing است که به اینگونه عمل میکند
یک ورودی که همان متن اصلی است را میگیرد و که به این بخش encoder میگویند
یک ورودی دیگر که به بخش decoder میرود و آن همان متن خلاصه شده است
که این را با معماری transformer انجام میدهیم:

حال نوبت به نوشتن positional encoder میرسد برای مشخص کردن اینکه هر کلمه در کجای جمله قرار دارد:
class PositionalEncoding(tf.keras.layers.Layer): def __init__(self, max_length, embed_size, dtype=tf.float32, **kwargs): super().__init__(dtype=dtype, **kwargs) assert embed_size % 2 == 0, "embed_size must be even" p, i = np.meshgrid(np.arange(max_length), 2 * np.arange(embed_size // 2)) pos_emb = np.empty((1, max_length, embed_size)) pos_emb[0, :, ::2] = np.sin(p / 10_000 ** (i / embed_size)).T pos_emb[0, :, 1::2] = np.cos(p / 10_000 ** (i / embed_size)).T self.pos_encodings = tf.constant(pos_emb.astype(self.dtype)) self.supports_masking = True def call(self, inputs): batch_max_length = tf.shape(inputs)[1] return inputs + self.pos_encodings[:, :batch_max_length]بعد از این نوبت به نوشتن انکودر میشود:
class Encoder(tf.keras.Model): def __init__(self, embed_size): super().__init__() self.encoder_embedding = tf.keras.layers.Embedding(vocab_size, embed_size, mask_zero=True) self.pos_embed_layer = tf.keras.layers.Embedding(max_length, embed_size) self.pos_en = PositionalEncoding(max_length, embed_size) self.att1 = tf.keras.layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_size, dropout=dropout_rate) self.norm1 = tf.keras.layers.LayerNormalization() self.Dense1 = tf.keras.layers.Dense(n_units, activation="relu") self.Dense2 = tf.keras.layers.Dense(embed_size) self.dropout = tf.keras.layers.Dropout(dropout_rate) self.norm2 = tf.keras.layers.LayerNormalization() def call(self, inputs, training=True): embed_outpot = self.encoder_embedding(inputs) pos_embed_layer = self.pos_en encoder_in = pos_embed_layer(embed_outpot) encoder_pad_mask = tf.math.not_equal(inputs, 0)[:, tf.newaxis] Z = encoder_in for _ in range(N): skip = Z attn_layer = self.att1 Z = attn_layer(Z, value=Z, attention_mask=encoder_pad_mask) Z = self.norm1(tf.keras.layers.Add()([Z, skip])) skip = Z Z = self.Dense1(Z) Z = self.Dense2(Z) Z = self.dropout(Z) Z = self.norm2(tf.keras.layers.Add()([Z, skip])) return Z,encoder_pad_maskو بعد از این decoder خود را میسازیم:
یک تفاوت در دادن به ورودی به دیکودر هست بعد از اینکه positional_encoding روی ورودی انجام میشود در انکودر آنها را mask میکنیم که آن مقدار هایی که مقدار صفر دارند در نظر گرفته نشون (paddings) ولی در انکودر علاوه برانجام این باید آن را causal هم کنیم چون در دیکودر ما نیاز به این نداریم بدانیم که بعد از این چه چیزی وجود دارد ولی در انکودر نیاز هست(مانند bidirectional rnn)
class Decoder(tf.keras.Model): def __init__(self, embed_size): super().__init__() self.embedding = tf.keras.layers.Embedding(vocab_size, embed_size, mask_zero=True) self.pos_de = PositionalEncoding(max_length, embed_size) self.mask_att = tf.keras.layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_size, dropout=dropout_rate) self.norm1 = tf.keras.layers.LayerNormalization() self.cross_att = tf.keras.layers.MultiHeadAttention( num_heads=num_heads, key_dim=embed_size, dropout=dropout_rate) self.norm2 = tf.keras.layers.LayerNormalization() self.Dense1 = tf.keras.layers.Dense(n_units, activation="relu") self.Dense2 = tf.keras.layers.Dense(embed_size) self.norm3 = tf.keras.layers.LayerNormalization() def call(self, inputs1, training = True): l, decoder_input_ids = inputs1 Z, encoder_pad_mask = l embedding_output = self.embedding(decoder_input_ids) pos_embed_layer = self.pos_de decoder_in = pos_embed_layer(embedding_output) decoder_pad_mask = tf.math.not_equal(decoder_input_ids, 0)[:, tf.newaxis] batch_max_len_dec = tf.shape(embedding_output)[1] causal_mask = tf.linalg.band_part( tf.ones((batch_max_len_dec, batch_max_len_dec), tf.bool), -1, 0) encoder_outputs = Z Z = decoder_in for _ in range(N): skip = Z attn_layer = self.mask_att Z = attn_layer(Z, value=Z, attention_mask=causal_mask & decoder_pad_mask) Z = self.norm1(tf.keras.layers.Add()([Z, skip])) skip = Z attn_layer = self.cross_att Z = attn_layer(Z, value=encoder_outputs, attention_mask=encoder_pad_mask) Z = self.norm2(tf.keras.layers.Add()([Z, skip])) skip = Z Z = self.Dense1(Z) Z = self.Dense2(Z) Z = self.norm3(tf.keras.layers.Add()([Z, skip])) return Zو حال نوبت به نوشتن کلاسی میرسد که در آن از encoder , decoder استفاده شود
class Summarizer(tf.keras.Model): def __init__(self, vocab_size, embed_size): super().__init__() self.encoder = Encoder(embed_size) self.decoder = Decoder(embed_size) self.output_layer = tf.keras.layers.Dense(vocab_size, activation='softmax') def call(self, inputs, trainging=True): encoder_inputs_ids, decoder_inputs_ids = inputs encoder_outputs= self.encoder(encoder_inputs_ids) Z = self.decoder((encoder_outputs, decoder_inputs_ids)) Y_proba = self.output_layer(Z) return Y_probamodel = Summarizer(vocab_size, embed_size)model.compile(loss="sparse_categorical_crossentropy", optimizer="nadam", metrics=["accuracy"])history = model.fit(train_ds, epochs=30, validation_data=val_ds, batch_size=128) تابعی برای inference time: def summaraize(sentense_en): summaraization = 'startofseq ' for word_idx in range(max_length): X = np.array([sentense_en]) X_dec = np.array([summaraization]) y_proba = model.predict((X, X_dec))[0, word_idx] predicted_word_idx = np.argmax(y_proba) predicted_word = summary_vectorization_layer.get_vocabulary()[predicted_word_idx] if predicted_word == 'endofseq': break summaraization += ' ' + predicted_word return summaraization.strip('startofseq ')