11"""Sequence-to-Sequence Auto Encoder."""
22import numpy as np
33
4- from keras .preprocessing .sequence import pad_sequences
4+ import keras .models
5+ from keras .models import Sequential
56from keras .optimizers import RMSprop
6- from keras .layers import Input , LSTM , RepeatVector
7- from keras .layers import Dropout
7+ from keras .layers import LSTM , RepeatVector
8+ from keras .layers .embeddings import Embedding
9+ from keras .layers .wrappers import TimeDistributed
10+ from keras .layers import Dense , Dropout , Activation
811from keras .models import Model
912
1013from yoctol_utils .hash import consistent_hash
1114
1215from .base import BaseSeq2Vec
1316from .base import TrainableInterfaceMixin
14-
17+ from .base import BaseTransformer
18+ from .util import generate_padding_array
1519
1620def _create_single_layer_seq2seq_model (
1721 max_length ,
1822 max_index ,
23+ embedding_size ,
1924 latent_size ,
2025 learning_rate ,
2126 rho = 0.9 ,
22- decay = 0.0 ,
27+ decay = 0.01 ,
2328 ):
24- inputs = Input (shape = (max_length , max_index ))
25- encoded = LSTM (latent_size )(inputs )
26- decoded = Dropout (0.3 )(encoded )
27- decoded = RepeatVector (max_length )(decoded )
28- decoded = LSTM (max_index , return_sequences = True )(decoded )
29- model = Model (inputs , decoded )
30- encoder = Model (inputs , encoded )
29+
30+ model = Sequential ()
31+ model .add (
32+ Embedding (
33+ max_index , embedding_size , input_length = max_length ,
34+ name = 'embedding' , mask_zero = True , dropout = 0.2
35+ )
36+ )
37+ model .add (
38+ LSTM (
39+ output_dim = latent_size , return_sequences = False ,
40+ name = 'en_LSTM_1' , dropout_W = 0.2 , dropout_U = 0.3
41+ )
42+ )
43+ model .add (
44+ RepeatVector (max_length )
45+ )
46+ model .add (
47+ LSTM (
48+ embedding_size , return_sequences = True ,
49+ name = 'de_LSTM_1' , dropout_W = 0.2 , dropout_U = 0.3
50+ )
51+ )
52+ model .add (
53+ TimeDistributed (Dense (max_index ))
54+ )
55+ model .add (Dropout (0.2 ))
56+ model .add (Activation ('softmax' ))
57+
58+ encoder = Model (
59+ model .input , model .get_layer ('en_LSTM_1' ).output
60+ )
3161
3262 optimizer = RMSprop (
3363 lr = learning_rate ,
@@ -46,6 +76,42 @@ def _one_hot_encode_seq(seq, max_index):
4676 np_seq .append (arr )
4777 return np_seq
4878
79+ def _hash_seq (sequence , max_index ):
80+ return [consistent_hash (word ) % max_index + 1 for word in sequence ]
81+
82+ class Seq2vecAutoEncoderInputTransformer (BaseTransformer ):
83+
84+ def __init__ (self , max_index , max_length ):
85+ self .max_index = max_index
86+ self .max_length = max_length
87+
88+ def seq_transform (self , seq ):
89+ return _hash_seq (seq , self .max_index )
90+
91+ def __call__ (self , seqs ):
92+ array = generate_padding_array (
93+ seqs , self .seq_transform , 0 , self .max_length , inverse = True
94+ )
95+ return array
96+
97+ class Seq2vecAutoEncoderOutputTransformer (BaseTransformer ):
98+
99+ def __init__ (self , max_index , max_length ):
100+ self .max_index = max_index
101+ self .max_length = max_length
102+
103+ def seq_transform (self , seq ):
104+ transformed_seq = _one_hot_encode_seq (
105+ _hash_seq (seq , self .max_index ), self .max_index
106+ )
107+ return transformed_seq
108+
109+ def __call__ (self , seqs ):
110+ array = generate_padding_array (
111+ seqs , self .seq_transform , np .zeros (self .max_index + 1 ),
112+ self .max_length , inverse = False
113+ )
114+ return array
49115
50116class Seq2SeqAutoEncoderUseWordHash (TrainableInterfaceMixin , BaseSeq2Vec ):
51117 """Hash words and feed to seq2seq auto-encoder.
@@ -68,60 +134,38 @@ def __init__(
68134 max_index ,
69135 max_length ,
70136 learning_rate = 0.0001 ,
137+ embedding_size = 64 ,
71138 latent_size = 20 ,
72139 ):
73140 self .max_index = max_index
74141 self .max_length = max_length
75142 self .learning_rate = learning_rate
143+ self .embedding_size = embedding_size
76144 self .latent_size = latent_size
77145
146+ self .input_transformer = Seq2vecAutoEncoderInputTransformer (
147+ max_index , max_length
148+ )
149+ self .output_transformer = Seq2vecAutoEncoderOutputTransformer (
150+ max_index , max_length
151+ )
152+
78153 model , encoder = _create_single_layer_seq2seq_model (
79154 max_length = self .max_length ,
80155 max_index = self .max_index + 1 ,
156+ embedding_size = self .embedding_size ,
81157 latent_size = self .latent_size ,
82158 learning_rate = self .learning_rate ,
83159 )
84160 self .model = model
85161 self .encoder = encoder
86162
87- def _hash_seq (self , sequence ):
88- return [consistent_hash (word ) % self .max_index + 1 for word in sequence ]
89-
90- def _generate_padding_array (self , seqs ):
91- hashed_seq = []
92- for seq in seqs :
93- hashed_seq .append (self ._hash_seq (seq ))
94- data_pad = pad_sequences (
95- hashed_seq ,
96- maxlen = self .max_length ,
97- value = 0 ,
98- )
99-
100- array = []
101- for seq in data_pad :
102- np_seq = _one_hot_encode_seq (seq , self .max_index )
103- array .append (np_seq )
104- return np .array (array )
105-
106- def fit (self , train_seqs , predict_seqs = None , verbose = 2 , nb_epoch = 10 , validation_split = 0.0 ):
107- train_x = self ._generate_padding_array (train_seqs )
108- if predict_seqs is None :
109- train_y = train_x
110- else :
111- train_y = self ._generate_padding_array (predict_seqs )
112- self .model .fit (
113- train_x , train_y ,
114- verbose = verbose ,
115- nb_epoch = nb_epoch ,
116- validation_split = validation_split ,
163+ def load_model (self , file_path ):
164+ self .model = keras .models .load_model (file_path )
165+ self .encoder = Model (
166+ self .model .input , self .model .get_layer ('en_LSTM_1' ).output
117167 )
118-
119- def transform (self , seqs ):
120- test_x = self ._generate_padding_array (seqs )
121- return self .encoder .predict (test_x )
122-
123- def transform_single_sequence (self , seq ):
124- return self .transform ([seq ])
125-
126- def __call__ (self , seqs ):
127- return self .transform (seqs )
168+ self .max_index = self .model .get_layer ('embedding' ).input_dim - 1
169+ self .max_length = self .model .input_shape [1 ]
170+ self .embedding_size = self .model .get_layer ('embedding' ).output_dim
171+ self .latent_size = self .model .get_layer ('en_LSTM_1' ).output_dim
0 commit comments