import os import pickle import numpy as np import tensorflow as tf def rnn_top(scope_name, rnn_rez, settings, classes_number): with tf.variable_scope(scope_name, reuse=tf.AUTO_REUSE) as scope: rez_size = settings['rnn_state_size'] w_softmax = tf.get_variable("W", (rez_size, classes_number)) b_softmax = tf.get_variable("b", [classes_number]) logits = tf.matmul(rnn_rez, w_softmax) + b_softmax return logits def rnn_cell_unit(settings, for_usage, keep_drop=None): cell = tf.contrib.rnn.GRUCell( num_units=settings['rnn_state_size'] ) if not for_usage and keep_drop is not None: cell = tf.contrib.rnn.DropoutWrapper( cell, input_keep_prob = keep_drop ) if 'use_residual' in settings and settings['use_residual']: cell = tf.nn.rnn_cell.ResidualWrapper(cell) return cell def rnn_cell(settings, for_usage, keep_drop=None): if settings['rnn_layers_count'] > 1: cells = [] for i in range(settings['rnn_layers_count']): with tf.variable_scope('RnnUnit_%s' % i, reuse=tf.AUTO_REUSE) as scope: cell = rnn_cell_unit(settings, for_usage, keep_drop) cells.append(cell) cell = tf.nn.rnn_cell.MultiRNNCell(cells) else: with tf.variable_scope('RnnUnit', reuse=tf.AUTO_REUSE) as scope: cell = rnn_cell_unit(settings, for_usage, keep_drop) return cell def build_rnn(rnn_input, keep_drop, seq_len, settings, initial_state_fw=None, initial_state_bw=None, for_usage=False, with_seq=False, top_concat=False): if settings['rnn_layers_count'] > 1 and initial_state_fw is not None: initial_state_fw = tuple([initial_state_fw for i in range(settings['rnn_layers_count'])]) if settings['rnn_layers_count'] > 1 and initial_state_bw is not None: initial_state_bw = tuple([initial_state_bw for i in range(settings['rnn_layers_count'])]) if settings['rnn_bidirectional']: with tf.variable_scope('Rnn', reuse=tf.AUTO_REUSE) as scope: with tf.variable_scope('FCell', reuse=tf.AUTO_REUSE) as scope: fw_cell = rnn_cell(settings, for_usage, keep_drop) with tf.variable_scope('BCell', reuse=tf.AUTO_REUSE) as scope: bw_cell = rnn_cell(settings, for_usage, keep_drop) seq_val, (final_fw, final_bw) = tf.nn.bidirectional_dynamic_rnn(cell_fw=fw_cell, cell_bw=bw_cell, sequence_length=seq_len, initial_state_fw = initial_state_fw, initial_state_bw = initial_state_bw, inputs=rnn_input, dtype=tf.float32) if settings['rnn_layers_count'] > 1: final_fw = final_fw[-1] final_bw = final_bw[-1] if top_concat: final_state = tf.concat([final_fw, final_bw], axis=1) final_state = tf.layers.dense(final_state, settings['rnn_state_size']) seq_val = tf.concat([seq_val[0], seq_val[1]], axis=2) seq_val = tf.layers.dense(seq_val, settings['rnn_state_size']) else: final_state = tf.add(final_fw, final_bw) seq_val = tf.add(seq_val[0], seq_val[1]) if with_seq: return final_state, seq_val else: return final_state else: with tf.variable_scope('Rnn', reuse=tf.AUTO_REUSE) as scope: cell = rnn_cell(settings, for_usage, keep_drop) rnn_rez, final_state = tf.nn.dynamic_rnn(cell=cell, sequence_length=seq_len, initial_state=initial_state_fw, inputs=rnn_input, dtype=tf.float32) return final_state[-1] def average_gradients(tower_grads): average_grads = [] for grad_and_vars in zip(*tower_grads): if any(x for x in grad_and_vars if x[0] is None): continue # Note that each grad_and_vars looks like the following: # ((grad0_gpu0, var0_gpu0), ... , (grad0_gpuN, var0_gpuN)) grads = [] for g, _ in grad_and_vars: # Add 0 dimension to the gradients to represent the tower. expanded_g = tf.expand_dims(g, 0) # Append on a 'tower' dimension which we will average over below. grads.append(expanded_g) # Average over the 'tower' dimension. grad = tf.concat(grads, 0) grad = tf.reduce_mean(grad, 0) # Keep in mind that the Variables are redundant because they are shared # across towers. So .. we will just return the first tower's pointer to # the Variable. v = grad_and_vars[0][1] grad_and_var = (grad, v) average_grads.append(grad_and_var) return average_grads def create_reset_metric(metric, scope='reset_metrics', *args, **metric_args): """ Source: https://github.com/tensorflow/tensorflow/issues/4814#issuecomment-314801758 Usage: epoch_loss, epoch_loss_update, epoch_loss_reset = create_reset_metric( tf.contrib.metrics.streaming_mean_squared_error, 'epoch_loss', predictions=output, labels=target) :param scope: :param metric_args: :return: """ with tf.variable_scope(scope) as scope: metric_op, update_op = metric(*args, **metric_args) vars = tf.contrib.framework.get_variables( scope, collection=tf.GraphKeys.LOCAL_VARIABLES) reset_op = tf.variables_initializer(vars) return metric_op, update_op, reset_op def load_cls_dataset(dataset_path, devices_count, type, batch_size, use_weights, gram="main"): path = os.path.join(dataset_path, f"{gram}_{type}_dataset.pkl") with open(path, 'rb') as f: items = pickle.load(f) batches = [items[i:i + batch_size] for i in range(0, len(items), batch_size)] cur_step = [] for batch in batches: x = np.stack([item['x'][0] for item in batch]) seq_len = np.asarray([item['x'][1] for item in batch], np.int) y = np.asarray([item['y'] for item in batch]) weight = np.asarray([item['weight'] for item in batch]) if use_weights else [1 for _ in range(batch)] if len(cur_step) == devices_count: yield cur_step cur_step = [] cur_step.append(dict( x=x, x_seq_len=seq_len, y=y, weight=weight )) if len(cur_step)==devices_count: yield cur_step def load_lemma_dataset(dataset_path, devices_count, type, batch_size): path = os.path.join(dataset_path, f"lemma_{type}_dataset.pkl") with open(path, 'rb') as f: items = pickle.load(f) batches = [items[i:i + batch_size] for i in range(0, len(items), batch_size)] cur_step = [] for batch in batches: x = np.stack([item['x'] for item in batch]) x_seq_len = np.asarray([item['x_len'] for item in batch], np.int) x_cls = np.asarray([item['main_cls'] for item in batch], np.int) y_seq_len = np.asarray([item['y_len'] for item in batch], np.int) max_len = y_seq_len.max() y = np.asarray([item['y'][:max_len] for item in batch]) x_src = [item['x_src'] for item in batch] y_src = [item['y_src'] for item in batch] if len(cur_step) == devices_count: yield cur_step cur_step = [] cur_step.append(dict( x=x, x_seq_len=x_seq_len, x_cls=x_cls, y=y, y_seq_len=y_seq_len, x_src=x_src, y_src=y_src )) if len(cur_step) == devices_count and all([len(step['x']) == batch_size for step in cur_step]): yield cur_step def load_inflect_dataset(dataset_path, devices_count, type, batch_size): path = os.path.join(dataset_path, f"inflect_{type}_dataset.pkl") with open(path, 'rb') as f: items = pickle.load(f) batches = [items[i:i + batch_size] for i in range(0, len(items), batch_size)] cur_step = [] for batch in batches: x = np.stack([item['x'] for item in batch]) x_seq_len = np.asarray([item['x_len'] for item in batch], np.int) x_cls = np.asarray([item['x_cls'] for item in batch], np.int) y_seq_len = np.asarray([item['y_len'] for item in batch], np.int) y_cls = np.asarray([item['y_cls'] for item in batch], np.int) max_len = y_seq_len.max() y = np.asarray([item['y'][:max_len] for item in batch]) x_src = [item['x_src'] for item in batch] y_src = [item['y_src'] for item in batch] if len(cur_step) == devices_count: yield cur_step cur_step = [] cur_step.append(dict( x=x, x_seq_len=x_seq_len, x_cls=x_cls, y=y, y_seq_len=y_seq_len, y_cls=y_cls, x_src=x_src, y_src=y_src )) if len(cur_step) == devices_count and all([len(step['x']) == batch_size for step in cur_step]): yield cur_step def seq2seq(graph_part, batch_size, x, x_init, x_seq_len, y, y_init, y_seq_len): start_tokens = tf.fill([batch_size], graph_part.start_char_index) initializer = tf.random_normal_initializer() y_seq_len += 1 with tf.variable_scope("Encoder", reuse=tf.AUTO_REUSE): encoder_char_embeddings = tf.get_variable( "CharEmbeddings", [graph_part.chars_count, graph_part.settings['char_vector_size']], initializer=initializer ) encoder_init_state = ClsGramEmbedder(graph_part.main_cls_dic, graph_part.settings['encoder']['gram_vector_size'], graph_part.settings['encoder']['ad_cls_vector_size'], graph_part.settings['encoder']['rnn_state_size'])(x_init, batch_size) encoder_input = tf.nn.embedding_lookup(encoder_char_embeddings, x) with tf.variable_scope("Decoder", reuse=tf.AUTO_REUSE): decoder_char_embeddings = tf.get_variable( "CharEmbeddings", [graph_part.chars_count, graph_part.settings['char_vector_size']], initializer=initializer ) decoder_init_state = ClsGramEmbedder(graph_part.main_cls_dic, graph_part.settings['decoder']['gram_vector_size'], graph_part.settings['decoder']['ad_cls_vector_size'], graph_part.settings['decoder']['rnn_state_size'])(y_init, batch_size) decoder_output = tf.nn.embedding_lookup(decoder_char_embeddings, y) if graph_part.for_usage: keep_drop = tf.constant(1, dtype=tf.float32, name='KeepDrop') decoder_keep_drop = tf.constant(1, dtype=tf.float32, name='DecoderKeepDrop') else: keep_drop = tf.placeholder(dtype=tf.float32, name='KeepDrop') decoder_keep_drop = tf.placeholder(dtype=tf.float32, name='DecoderKeepDrop') with tf.variable_scope('Encoder', reuse=tf.AUTO_REUSE) as scope: _, encoder_output = build_rnn( encoder_input, keep_drop, x_seq_len, graph_part.settings['encoder'], encoder_init_state, encoder_init_state, top_concat=False, for_usage=graph_part.for_usage, with_seq=True ) with tf.variable_scope('Decoder', reuse=tf.AUTO_REUSE) as scope: if not graph_part.for_usage: start_tokens_emd = tf.nn.embedding_lookup(decoder_char_embeddings, start_tokens) start_tokens_emd = tf.reshape(start_tokens_emd, (batch_size, -1, graph_part.settings['char_vector_size'])) decoder_output = tf.concat(values=[start_tokens_emd, decoder_output], axis=1) end_tokens = tf.fill([batch_size], graph_part.end_char_index) end_tokens_emd = tf.nn.embedding_lookup(decoder_char_embeddings, end_tokens) end_tokens_emd = tf.reshape(end_tokens_emd, (batch_size, -1, graph_part.settings['char_vector_size'])) decoder_output = tf.concat([decoder_output, end_tokens_emd], axis=1) end_tokens = tf.reshape(end_tokens, (batch_size, 1)) y = tf.concat([y, end_tokens], axis=1) if graph_part.for_usage: helper = tf.contrib.seq2seq.GreedyEmbeddingHelper(decoder_char_embeddings, start_tokens=start_tokens, end_token=graph_part.end_char_index) else: helper = tf.contrib.seq2seq.TrainingHelper(decoder_output, y_seq_len) attention_mechanism = tf.contrib.seq2seq.BahdanauAttention( num_units=graph_part.settings['decoder']['rnn_state_size'], memory=encoder_output, memory_sequence_length=x_seq_len, normalize=False ) cell = rnn_cell(graph_part.settings['decoder'], graph_part.for_usage, decoder_keep_drop) cell = tf.contrib.seq2seq.AttentionWrapper( cell, attention_mechanism, attention_layer_size=graph_part.settings['decoder']['rnn_state_size'] / 2 ) out_cell = tf.contrib.rnn.OutputProjectionWrapper( cell, graph_part.chars_count ) init_state = cell.zero_state(dtype=tf.float32, batch_size=batch_size).clone(cell_state=decoder_init_state) decoder = tf.contrib.seq2seq.BasicDecoder( cell=out_cell, helper=helper, initial_state=init_state ) max_len = graph_part.settings['max_length'] if graph_part.for_usage else tf.reduce_max(y_seq_len) outputs = tf.contrib.seq2seq.dynamic_decode( decoder=decoder, impute_finished=True, output_time_major=False, maximum_iterations=max_len ) decoder_ids = outputs[0].sample_id if not graph_part.for_usage: decoder_logits = outputs[0].rnn_output masks = tf.sequence_mask( lengths=y_seq_len, dtype=tf.float32, maxlen=tf.reduce_max(y_seq_len) ) seq_mask_int = tf.cast(masks, tf.int32) seq_mask_flat = tf.cast(tf.reshape(masks, (-1,)), tf.int32) # seq loss loss = tf.contrib.seq2seq.sequence_loss( decoder_logits, y, masks, name="SeqLoss" ) vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope=graph_part.main_scope_name) grads = graph_part.optimiser.compute_gradients(loss, var_list=vars) graph_part.create_mean_metric(0, loss) labels_flat = tf.reshape(y, (-1,)) predictions_flat = tf.reshape(decoder_ids, (-1,)) # char accuracy nonzero_indices = tf.where(tf.not_equal(seq_mask_flat, 0)) labels_flat = tf.gather(labels_flat, nonzero_indices) labels_flat = tf.reshape(labels_flat, (-1,)) predictions_flat = tf.gather(predictions_flat, nonzero_indices) predictions_flat = tf.reshape(predictions_flat, (-1,)) # remove -1 items where no sampling took place sample_indexes = tf.where(tf.not_equal(predictions_flat, -1)) labels_flat = tf.gather(labels_flat, sample_indexes) predictions_flat = tf.gather(predictions_flat, sample_indexes) graph_part.create_accuracy_metric(1, labels_flat, predictions_flat) # seq accuracy labels = y * seq_mask_int predictions = decoder_ids * seq_mask_int labels_flat = tf.reshape(labels, (-1,)) predictions_flat = tf.reshape(predictions, (-1,)) sample_zeros = tf.cast(tf.not_equal(predictions_flat, -1), tf.int32) predictions = predictions_flat * sample_zeros labels = labels_flat * sample_zeros predictions = tf.reshape(predictions, (batch_size, max_len)) labels = tf.reshape(labels, (batch_size, max_len)) delta = labels - predictions labels = tf.reduce_sum(delta * delta, 1) predictions = tf.zeros(batch_size) graph_part.create_accuracy_metric(2, labels, predictions) graph_part.dev_grads.append(grads) graph_part.losses.append(loss) graph_part.keep_drops.append(keep_drop) graph_part.decoder_keep_drops.append(decoder_keep_drop) graph_part.results.append(decoder_ids) class ClsGramEmbedder: def __init__(self, cls_dic, gram_vector_size, ad_cls_vector_size, rez_size): gram_rez_dict = {} tpls = sorted([(key, cls_dic[key]) for key in cls_dic], key=lambda x: x[1]) cls_vectors = [] for cls_key, cls_index in tpls: cls_vector = [] for gram, gram_index in enumerate(list(cls_key)): gram_key = (gram_index, gram) if gram_key not in gram_rez_dict: gram_rez_dict[gram_key] = len(gram_rez_dict) cls_vector.append(gram_rez_dict[gram_key]) cls_vectors.append(cls_vector) tpls = sorted([(key, gram_rez_dict[key]) for key in gram_rez_dict], key=lambda x: x[1]) gram_vectors = [] for gram_key, gram_index in tpls: if gram_key[0] is None: val = np.zeros(gram_vector_size, dtype=np.float32) else: val = np.random.rand(gram_vector_size).astype(np.float32) gram_vectors.append(val) self.rez_size = rez_size self.classes_count = len(cls_dic) self.cls_vectors = np.asarray(cls_vectors, dtype=np.int) self.grams_count = len(gram_vectors) self.gram_vector_size = gram_vector_size self.gram_vectors = np.stack(gram_vectors) self.ad_cls_vector_size = ad_cls_vector_size def __call__(self, cls_pl, batch_size): # [self.grams_count, self.gram_vector_size], gram_embeddings = tf.get_variable( "GramEmbeddings", initializer=tf.constant(self.gram_vectors), dtype=tf.float32 ) # [self.classes_count, self.grams_count], cls_embeddings = tf.get_variable( "ClsEmbeddings", initializer=tf.constant(self.cls_vectors), dtype=tf.int64 ) gram_rez = tf.nn.embedding_lookup(cls_embeddings, cls_pl) gram_rez = tf.reshape(gram_rez, (-1, )) gram_rez = tf.nn.embedding_lookup(gram_embeddings, gram_rez) gram_rez = tf.reshape(gram_rez, (batch_size, -1)) ad_cls_embeddings = tf.get_variable( "AdClsEmbeddings", [self.classes_count, self.ad_cls_vector_size], initializer=tf.contrib.layers.xavier_initializer() ) ad_cls_rez = tf.nn.embedding_lookup(ad_cls_embeddings, cls_pl) result = tf.concat([gram_rez, ad_cls_rez], axis=1) return result