├── .gitignore ├── README.md ├── __init__.py ├── argparse_test.py ├── models ├── __init__.py ├── custom_lstm.py ├── seq_encoder.py ├── seqgan.py └── text_cnn.py ├── nico_comment ├── dis_profiler.ipynb ├── generate.py ├── run_sequence_gan.py ├── summary_test.py ├── vae_analysis.ipynb └── vrae.py ├── novelist_ni_narou ├── dataset │ ├── __init__.py │ ├── arasuji.py │ └── scrape.py ├── generate.py ├── run_sequence_gan.py └── seq2seq.py ├── optimizer_hook ├── __init__.py └── named_weight_decay.py └── oracle_test ├── README.md ├── run_sequence_gan.py ├── save ├── real_data.txt └── target_params.pkl └── utils_oracle.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | *.ipynb 8 | 9 | test/ 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | env/ 17 | build/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *,cover 51 | .hypothesis/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | 61 | # Flask instance folder 62 | instance/ 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # IPython Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | venv/ 87 | ENV/ 88 | 89 | # Spyder project settings 90 | .spyderproject 91 | 92 | # Rope project settings 93 | .ropeproject 94 | 95 | .idea/ 96 | 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chainer-SeqGAN 2 | 3 | - implementation of [SeqGAN: Sequence Generative Adversarial Nets with Policy Gradient](https://arxiv.org/abs/1609.05473) 4 | - Complete oracle test in this paper 5 | 6 | ## requirements 7 | 8 | - Python > 3.4 9 | - Chainer > 1.5 10 | - Tensorflow (CPU-only) 11 | ``` 12 | # Ubuntu/Linux 64-bit Python 3.4 13 | export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.11.0-cp34-cp34m-linux_x86_64.whl 14 | 15 | # Ubuntu/Linux 64-bit Python 3.5 16 | export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.11.0-cp35-cp35m-linux_x86_64.whl 17 | 18 | # Mac OS X, CPU only, Python 3.4 or 3.5: 19 | export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-0.11.0-py3-none-any.whl 20 | 21 | pip install $TF_BINARY_URL 22 | ``` 23 | 24 | ## Usage 25 | 26 | ``` 27 | cd oracle_test && python run_sequence_gan.py 28 | ``` 29 | 30 | 31 | Any advice or suggestion is strongly welcomed in issues. 32 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fukuta0614/chainer-SeqGAN/85aa70b1b476346138e22058d527b669ee309203/__init__.py -------------------------------------------------------------------------------- /argparse_test.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fukuta0614/chainer-SeqGAN/85aa70b1b476346138e22058d527b669ee309203/argparse_test.py -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import seqgan 2 | from . import text_cnn 3 | from . import seq_encoder 4 | 5 | SeqGAN = seqgan.SeqGAN 6 | TextCNN = text_cnn.TextCNN 7 | SeqEncoder = seq_encoder.SeqEncoder -------------------------------------------------------------------------------- /models/custom_lstm.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import six 3 | 4 | import chainer 5 | from chainer.functions.activation import lstm 6 | from chainer.functions.array import concat 7 | from chainer.functions.array import split_axis 8 | from chainer import initializers 9 | from chainer import link 10 | from chainer.links.connection import linear 11 | from chainer import variable 12 | 13 | 14 | class LSTMBase(link.Chain): 15 | 16 | def __init__(self, in_size, out_size, 17 | lateral_init=None, upward_init=None, 18 | bias_init=0, forget_bias_init=0): 19 | super(LSTMBase, self).__init__( 20 | upward=linear.Linear(in_size, 4 * out_size, initialW=0), 21 | lateral=linear.Linear(out_size, 4 * out_size, 22 | initialW=0, nobias=True), 23 | ) 24 | self.state_size = out_size 25 | self.lateral_init = lateral_init 26 | self.upward_init = upward_init 27 | self.bias_init = bias_init 28 | self.forget_bias_init = forget_bias_init 29 | 30 | if in_size is not None: 31 | self._initialize_params() 32 | 33 | def _initialize_params(self): 34 | for i in six.moves.range(0, 4 * self.state_size, self.state_size): 35 | initializers.init_weight( 36 | self.lateral.W.data[i:i + self.state_size, :], 37 | self.lateral_init) 38 | initializers.init_weight( 39 | self.upward.W.data[i:i + self.state_size, :], self.upward_init) 40 | 41 | a, i, f, o = lstm._extract_gates( 42 | self.upward.b.data.reshape(1, 4 * self.state_size, 1)) 43 | initializers.init_weight(a, self.bias_init) 44 | initializers.init_weight(i, self.bias_init) 45 | initializers.init_weight(f, self.forget_bias_init) 46 | initializers.init_weight(o, self.bias_init) 47 | 48 | 49 | class LSTM(LSTMBase): 50 | 51 | """Fully-connected LSTM layer. 52 | 53 | This is a fully-connected LSTM layer as a chain. Unlike the 54 | :func:`~chainer.functions.lstm` function, which is defined as a stateless 55 | activation function, this chain holds upward and lateral connections as 56 | child links. 57 | 58 | It also maintains *states*, including the cell state and the output 59 | at the previous time step. Therefore, it can be used as a *stateful LSTM*. 60 | 61 | This link supports variable length inputs. The mini-batch size of the 62 | current input must be equal to or smaller than that of the previous one. 63 | The mini-batch size of ``c`` and ``h`` is determined as that of the first 64 | input ``x``. 65 | When mini-batch size of ``i``-th input is smaller than that of the previous 66 | input, this link only updates ``c[0:len(x)]`` and ``h[0:len(x)]`` and 67 | doesn't change the rest of ``c`` and ``h``. 68 | So, please sort input sequences in descending order of lengths before 69 | applying the function. 70 | 71 | Args: 72 | in_size (int): Dimension of input vectors. If ``None``, parameter 73 | initialization will be deferred until the first forward data pass 74 | at which time the size will be determined. 75 | out_size (int): Dimensionality of output vectors. 76 | lateral_init: A callable that takes ``numpy.ndarray`` or 77 | ``cupy.ndarray`` and edits its value. 78 | It is used for initialization of the lateral connections. 79 | Maybe be ``None`` to use default initialization. 80 | upward_init: A callable that takes ``numpy.ndarray`` or 81 | ``cupy.ndarray`` and edits its value. 82 | It is used for initialization of the upward connections. 83 | Maybe be ``None`` to use default initialization. 84 | bias_init: A callable that takes ``numpy.ndarray`` or 85 | ``cupy.ndarray`` and edits its value 86 | It is used for initialization of the biases of cell input, 87 | input gate and output gate.and gates of the upward connection. 88 | Maybe a scalar, in that case, the bias is 89 | initialized by this value. 90 | Maybe be ``None`` to use default initialization. 91 | forget_bias_init: A callable that takes ``numpy.ndarray`` or 92 | ``cupy.ndarray`` and edits its value 93 | It is used for initialization of the biases of the forget gate of 94 | the upward connection. 95 | Maybe a scalar, in that case, the bias is 96 | initialized by this value. 97 | Maybe be ``None`` to use default initialization. 98 | 99 | 100 | Attributes: 101 | upward (~chainer.links.Linear): Linear layer of upward connections. 102 | lateral (~chainer.links.Linear): Linear layer of lateral connections. 103 | c (~chainer.Variable): Cell states of LSTM units. 104 | h (~chainer.Variable): Output at the previous time step. 105 | 106 | """ 107 | 108 | def __init__(self, in_size, out_size, **kwargs): 109 | super(LSTM, self).__init__(in_size, out_size, **kwargs) 110 | self.reset_state() 111 | 112 | def to_cpu(self): 113 | super(LSTM, self).to_cpu() 114 | if self.c is not None: 115 | self.c.to_cpu() 116 | if self.h is not None: 117 | self.h.to_cpu() 118 | 119 | def to_gpu(self, device=None): 120 | super(LSTM, self).to_gpu(device) 121 | if self.c is not None: 122 | self.c.to_gpu(device) 123 | if self.h is not None: 124 | self.h.to_gpu(device) 125 | 126 | def set_state(self, c, h): 127 | """Sets the internal state. 128 | 129 | It sets the :attr:`c` and :attr:`h` attributes. 130 | 131 | Args: 132 | c (~chainer.Variable): A new cell states of LSTM units. 133 | h (~chainer.Variable): A new output at the previous time step. 134 | 135 | """ 136 | assert isinstance(c, chainer.Variable) 137 | assert isinstance(h, chainer.Variable) 138 | c_ = c 139 | h_ = h 140 | if self.xp == numpy: 141 | c_.to_cpu() 142 | h_.to_cpu() 143 | else: 144 | c_.to_gpu() 145 | h_.to_gpu() 146 | self.c = c_ 147 | self.h = h_ 148 | 149 | def reset_state(self): 150 | """Resets the internal state. 151 | 152 | It sets ``None`` to the :attr:`c` and :attr:`h` attributes. 153 | 154 | """ 155 | self.c = self.h = None 156 | 157 | def __call__(self, x, z): 158 | """Updates the internal state and returns the LSTM outputs. 159 | 160 | Args: 161 | x (~chainer.Variable): A new batch from the input sequence. 162 | 163 | Returns: 164 | ~chainer.Variable: Outputs of updated LSTM units. 165 | 166 | """ 167 | if self.upward.has_uninitialized_params: 168 | in_size = x.size // x.shape[0] 169 | self.upward._initialize_params(in_size) 170 | self._initialize_params() 171 | 172 | batch = x.shape[0] 173 | lstm_in = self.upward(x) + z # add latent variable 174 | h_rest = None 175 | if self.h is not None: 176 | h_size = self.h.shape[0] 177 | if batch == 0: 178 | h_rest = self.h 179 | elif h_size < batch: 180 | msg = ('The batch size of x must be equal to or less than the ' 181 | 'size of the previous state h.') 182 | raise TypeError(msg) 183 | elif h_size > batch: 184 | h_update, h_rest = split_axis.split_axis( 185 | self.h, [batch], axis=0) 186 | lstm_in += self.lateral(h_update) 187 | else: 188 | lstm_in += self.lateral(self.h) 189 | if self.c is None: 190 | xp = self.xp 191 | self.c = variable.Variable( 192 | xp.zeros((batch, self.state_size), dtype=x.dtype), 193 | volatile='auto') 194 | self.c, y = lstm.lstm(self.c, lstm_in) 195 | 196 | if h_rest is None: 197 | self.h = y 198 | elif len(y.data) == 0: 199 | self.h = h_rest 200 | else: 201 | self.h = concat.concat([y, h_rest], axis=0) 202 | 203 | return y 204 | -------------------------------------------------------------------------------- /models/seq_encoder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import chainer 4 | import chainer.functions as F 5 | import chainer.links as L 6 | from chainer.cuda import cupy as xp 7 | import numpy as np 8 | 9 | 10 | class SeqEncoder(chainer.Chain): 11 | def __init__(self, vocab_size, emb_dim, hidden_dim, sequence_length, tag_num=0, latent_dim=None): 12 | self.vocab_size = vocab_size 13 | self.emb_dim = emb_dim 14 | self.hidden_dim = hidden_dim 15 | self.latent_dim = latent_dim if latent_dim else hidden_dim 16 | self.sequence_length = sequence_length 17 | self.input_dim = self.hidden_dim + tag_num 18 | 19 | if tag_num > 0: 20 | super(SeqEncoder, self).__init__( 21 | tag_embed=L.EmbedID(tag_num, tag_num, initialW=np.random.normal(scale=0.1, size=(tag_num, tag_num))), 22 | embed=L.EmbedID(self.vocab_size, self.emb_dim, initialW=np.random.normal(scale=0.1, size=(self.vocab_size, self.emb_dim))), 23 | lstm1=L.LSTM(self.emb_dim, self.hidden_dim), 24 | linear_mu=L.Linear(self.input_dim, self.latent_dim), 25 | linear_ln_var=L.Linear(self.input_dim, self.latent_dim) 26 | ) 27 | else: 28 | super(SeqEncoder, self).__init__( 29 | embed=L.EmbedID(self.vocab_size, self.emb_dim, 30 | initialW=np.random.normal(scale=0.1, size=(self.vocab_size, self.emb_dim))), 31 | lstm1=L.LSTM(self.emb_dim, self.hidden_dim), 32 | linear_mu=L.Linear(self.input_dim, self.latent_dim), 33 | linear_ln_var=L.Linear(self.input_dim, self.latent_dim) 34 | ) 35 | 36 | def reset_state(self): 37 | if hasattr(self, "lstm1"): 38 | self.lstm1.reset_state() 39 | if hasattr(self, "lstm2"): 40 | self.lstm2.reset_state() 41 | 42 | def encode(self, x_input, train=True): 43 | """ 44 | inputを逆順にいれる 45 | """ 46 | self.reset_state() 47 | for i in range(self.sequence_length): 48 | x = chainer.Variable(xp.asanyarray(x_input[:, self.sequence_length-i-1], 'int32'), volatile=not train) 49 | h0 = self.embed(x) 50 | h1 = self.lstm1(F.dropout(h0, train=train)) 51 | 52 | mu = self.linear_mu(h1) 53 | ln_var = self.linear_ln_var(h1) 54 | 55 | return h1, mu, ln_var 56 | 57 | def encode_with_tag(self, x_input, tag, train=True): 58 | """ 59 | inputを逆順にいれる 60 | """ 61 | self.reset_state() 62 | for i in range(self.sequence_length): 63 | x = chainer.Variable(xp.asanyarray(x_input[:, self.sequence_length-i-1], 'int32'), volatile=not train) 64 | h0 = self.embed(x) 65 | h1 = self.lstm1(F.dropout(h0, train=train)) 66 | tag_ = self.tag_embed(chainer.Variable(self.xp.array(tag, 'int32'), volatile=not train)) 67 | h1 = F.concat((h1, tag_)) 68 | 69 | mu = self.linear_mu(h1) 70 | ln_var = self.linear_ln_var(h1) 71 | 72 | return h1, mu, ln_var 73 | -------------------------------------------------------------------------------- /models/seqgan.py: -------------------------------------------------------------------------------- 1 | import chainer 2 | import chainer.functions as F 3 | import chainer.links as L 4 | from chainer import cuda 5 | import numpy as np 6 | # import temptemp 7 | 8 | 9 | def choice(t): 10 | return np.random.choice(t[0], p=t[1]) 11 | 12 | 13 | class SeqGAN(chainer.Chain): 14 | def __init__(self, sequence_length, vocab_size, emb_dim, hidden_dim, start_token, reward_gamma=0.95, lstm_layer=1, 15 | dropout=False, oracle=False, free_pretrain=False, encoder=None, latent_dim=None, tag_dim=0): 16 | 17 | self.vocab_size = vocab_size 18 | self.emb_dim = emb_dim 19 | self.hidden_dim = hidden_dim 20 | self.sequence_length = sequence_length 21 | self.start_token = start_token 22 | self.x0 = None 23 | self.reward_gamma = reward_gamma 24 | self.g_params = [] 25 | self.d_params = [] 26 | self.oracle = oracle 27 | self.dropout = dropout 28 | self.temperature = 1.0 29 | self.state = {} 30 | self.free_pretrain = free_pretrain 31 | self.encoder = encoder 32 | self.latent_dim = latent_dim if latent_dim else 0 33 | self.input_dim = self.emb_dim + self.latent_dim 34 | self.dropout_ratio = 0.5 35 | 36 | layers = dict() 37 | layers['embed'] = L.EmbedID(self.vocab_size, self.emb_dim, 38 | initialW=np.random.normal(scale=0.1, size=(self.vocab_size, self.emb_dim))) 39 | if tag_dim > 0: 40 | if encoder: 41 | self.tag_num = tag_dim 42 | layers['tag_embed'] = L.EmbedID(self.tag_num, self.tag_num, 43 | initialW=np.random.normal(scale=0.1, size=(self.tag_num, self.tag_num))) 44 | dec_input = self.tag_num + self.hidden_dim 45 | layers['dec_input'] = L.Linear(dec_input, self.hidden_dim) 46 | else: 47 | self.tag_num = tag_dim 48 | layers['tag_embed'] = L.EmbedID(self.tag_num, self.hidden_dim) 49 | 50 | if self.oracle: 51 | layers['lstm1'] = L.LSTM(self.input_dim, self.hidden_dim, 52 | lateral_init=chainer.initializers.normal.Normal(0.1), 53 | upward_init=chainer.initializers.normal.Normal(0.1), 54 | bias_init=chainer.initializers.normal.Normal(0.1), 55 | forget_bias_init=chainer.initializers.normal.Normal(0.1) 56 | ) 57 | else: 58 | layers['lstm1'] = L.LSTM(self.input_dim, self.hidden_dim) 59 | if lstm_layer >= 2: 60 | layers['lstm2'] = L.LSTM(self.hidden_dim, self.hidden_dim) 61 | if lstm_layer >= 3: 62 | layers['lstm3'] = L.LSTM(self.hidden_dim, self.hidden_dim) 63 | if lstm_layer >= 4: 64 | layers['lstm4'] = L.LSTM(self.hidden_dim, self.hidden_dim) 65 | layers['out'] = L.Linear(self.hidden_dim, self.vocab_size, initialW=np.random.normal(scale=0.1, size=(self.vocab_size, self.hidden_dim))) 66 | 67 | super(SeqGAN, self).__init__(**layers) 68 | 69 | def reset_state(self): 70 | if hasattr(self, "lstm1"): 71 | self.lstm1.reset_state() 72 | if hasattr(self, "lstm2"): 73 | self.lstm2.reset_state() 74 | if hasattr(self, "lstm3"): 75 | self.lstm3.reset_state() 76 | if hasattr(self, "lstm4"): 77 | self.lstm4.reset_state() 78 | 79 | def save_state(self): 80 | if hasattr(self, "lstm1"): 81 | self.state['c1'] = self.lstm1.c 82 | self.state['h1'] = self.lstm1.h 83 | if hasattr(self, "lstm2"): 84 | self.state['c2'] = self.lstm2.c 85 | self.state['h2'] = self.lstm2.h 86 | if hasattr(self, "lstm3"): 87 | self.state['c3'] = self.lstm3.c 88 | self.state['h3'] = self.lstm3.h 89 | if hasattr(self, "lstm4"): 90 | self.state['c4'] = self.lstm4.c 91 | self.state['h4'] = self.lstm4.h 92 | 93 | def set_state(self): 94 | if hasattr(self, "lstm1"): 95 | self.lstm1.set_state(chainer.Variable(self.state['c1'].data.copy(), volatile=True), 96 | chainer.Variable(self.state['h1'].data.copy(), volatile=True)) 97 | if hasattr(self, "lstm2"): 98 | self.lstm2.set_state(chainer.Variable(self.state['c2'].data.copy(), volatile=True), 99 | chainer.Variable(self.state['h2'].data.copy(), volatile=True)) 100 | if hasattr(self, "lstm3"): 101 | self.lstm3.set_state(chainer.Variable(self.state['c3'].data.copy(), volatile=True), 102 | chainer.Variable(self.state['h3'].data.copy(), volatile=True)) 103 | if hasattr(self, "lstm4"): 104 | self.lstm4.set_state(chainer.Variable(self.state['c4'].data.copy(), volatile=True), 105 | chainer.Variable(self.state['h4'].data.copy(), volatile=True)) 106 | 107 | def decode_one_step(self, x, train=True, z=None): 108 | if self.dropout: 109 | if z is not None: 110 | h0 = F.concat((self.embed(x), z)) 111 | elif len(x.data.shape) == 2: 112 | h0 = x 113 | else: 114 | h0 = self.embed(x) 115 | 116 | h = self.lstm1(F.dropout(h0, ratio=self.dropout_ratio, train=train)) 117 | if hasattr(self, "lstm2"): 118 | h = self.lstm2(F.dropout(h, ratio=self.dropout_ratio, train=train)) 119 | if hasattr(self, "lstm3"): 120 | h = self.lstm3(F.dropout(h, ratio=self.dropout_ratio, train=train)) 121 | if hasattr(self, "lstm4"): 122 | h = self.lstm4(F.dropout(h, ratio=self.dropout_ratio, train=train)) 123 | y = self.out(h) 124 | return y 125 | else: 126 | if z is not None: 127 | h0 = F.concat((self.embed(x), z)) 128 | elif len(x.data.shape) == 2: 129 | h0 = x 130 | else: 131 | h0 = self.embed(x) 132 | 133 | h = self.lstm1(h0) 134 | if hasattr(self, "lstm2"): 135 | h = self.lstm2(h) 136 | if hasattr(self, "lstm3"): 137 | h = self.lstm3(h) 138 | if hasattr(self, "lstm4"): 139 | h = self.lstm4(h) 140 | y = self.out(h) 141 | return y 142 | 143 | def generate_use_tag(self, tag, x=None, pool=None, train=False): 144 | """ 145 | :return: (batch_size, self.seq_length) 146 | """ 147 | 148 | self.reset_state() 149 | batch_size = len(tag) 150 | if train: 151 | self.lstm1.h = self.tag_embed(chainer.Variable(self.xp.array(tag, 'int32'))) 152 | 153 | else: 154 | if x is not None: 155 | _, mu_z, ln_var_z = self.encoder.encode_with_tag(x, tag, train) 156 | z = F.gaussian(mu_z, ln_var_z) 157 | else: 158 | z = chainer.Variable(self.xp.asanyarray(np.random.normal(scale=1, size=(batch_size, self.emb_dim)), 'float32'), volatile=not train) 159 | 160 | if self.encoder: 161 | tag_ = self.tag_embed(chainer.Variable(self.xp.array(tag, 'int32'), volatile=not train)) 162 | self.lstm1.h = self.dec_input(F.concat((z, tag_))) 163 | else: 164 | self.lstm1.h = self.tag_embed(chainer.Variable(self.xp.array(tag, 'int32'), volatile=not train)) 165 | 166 | x = chainer.Variable(self.xp.asanyarray([self.start_token] * batch_size, 'int32'), volatile=not train) 167 | 168 | gen_x = np.zeros((batch_size, self.sequence_length), 'int32') 169 | 170 | for i in range(self.sequence_length): 171 | scores = self.decode_one_step(x, train) 172 | pred = F.softmax(scores) 173 | pred = cuda.to_cpu(pred.data) 174 | # pred = cuda.to_cpu(pred.data) - np.finfo(np.float32).epsneg 175 | 176 | if pool: 177 | generated = pool.map(choice, [(self.vocab_size, p) for p in pred]) 178 | else: 179 | generated = [np.random.choice(self.vocab_size, p=pred[j]) for j in range(batch_size)] 180 | # generated = [] 181 | # for j in range(batch_size): 182 | # # histogram = np.random.multinomial(1, pred[j]) 183 | # # generated.append(int(np.nonzero(histogram)[0])) 184 | 185 | gen_x[:, i] = generated 186 | x = chainer.Variable(self.xp.asanyarray(generated, 'int32'), volatile=not train) 187 | 188 | return gen_x 189 | 190 | def generate(self, batch_size, train=False, pool=None, random_input=False, random_state=False): 191 | """ 192 | :return: (batch_size, self.seq_length) 193 | """ 194 | 195 | self.reset_state() 196 | z = None 197 | 198 | if self.latent_dim: 199 | z = chainer.Variable(self.xp.asanyarray(np.random.normal(scale=1, size=(batch_size, self.latent_dim)), 'float32'), volatile=True) 200 | if random_input: 201 | self.x0 = np.random.normal(scale=1, size=(batch_size, self.emb_dim)) 202 | x = chainer.Variable(self.xp.asanyarray(self.x0, 'float32'), volatile=True) 203 | elif random_state: 204 | self.lstm1.h = chainer.Variable(self.xp.asanyarray(np.random.normal(scale=1, size=(batch_size, self.emb_dim)), 'float32'), volatile=True) 205 | x = chainer.Variable(self.xp.asanyarray([self.start_token] * batch_size, 'int32'), volatile=True) 206 | else: 207 | x = chainer.Variable(self.xp.asanyarray([self.start_token] * batch_size, 'int32'), volatile=True) 208 | 209 | gen_x = np.zeros((batch_size, self.sequence_length), 'int32') 210 | 211 | for i in range(self.sequence_length): 212 | scores = self.decode_one_step(x, train, z) 213 | pred = F.softmax(scores) 214 | pred = cuda.to_cpu(pred.data) 215 | # pred = cuda.to_cpu(pred.data) - np.finfo(np.float32).epsneg 216 | 217 | if pool: 218 | generated = pool.map(choice, [(self.vocab_size, p) for p in pred]) 219 | else: 220 | generated = [np.random.choice(self.vocab_size, p=pred[j]) for j in range(batch_size)] 221 | # generated = [] 222 | # for j in range(batch_size): 223 | # # histogram = np.random.multinomial(1, pred[j]) 224 | # # generated.append(int(np.nonzero(histogram)[0])) 225 | 226 | gen_x[:, i] = generated 227 | x = chainer.Variable(self.xp.asanyarray(generated, 'int32'), volatile=True) 228 | 229 | return gen_x 230 | 231 | def pretrain_step_vrae_tag(self, x_input, tag, word_drop_ratio=0.0, train=True): 232 | """ 233 | Maximum likelihood Estimation 234 | 235 | :param x_input: 236 | :return: loss 237 | """ 238 | batch_size = len(x_input) 239 | _, mu_z, ln_var_z = self.encoder.encode_with_tag(x_input, tag, train) 240 | 241 | self.reset_state() 242 | 243 | if self.latent_dim: 244 | z = F.gaussian(mu_z, ln_var_z) 245 | else: 246 | latent = F.gaussian(mu_z, ln_var_z) 247 | tag_ = self.tag_embed(chainer.Variable(self.xp.array(tag, 'int32'), volatile=not train)) 248 | self.lstm1.h = self.dec_input(F.concat((latent, tag_))) 249 | z = None 250 | 251 | accum_loss = 0 252 | for i in range(self.sequence_length): 253 | if i == 0: 254 | x = chainer.Variable(self.xp.asanyarray([self.start_token] * batch_size, 'int32'), volatile=not train) 255 | else: 256 | if np.random.random() < word_drop_ratio and train: 257 | x = chainer.Variable(self.xp.asanyarray([self.start_token] * batch_size, 'int32'), volatile=not train) 258 | else: 259 | x = chainer.Variable(self.xp.asanyarray(x_input[:, i - 1], 'int32'), volatile=not train) 260 | 261 | scores = self.decode_one_step(x, z=z) 262 | loss = F.softmax_cross_entropy(scores, chainer.Variable(self.xp.asanyarray(x_input[:, i], 'int32'), volatile=not train)) 263 | accum_loss += loss 264 | 265 | dec_loss = accum_loss 266 | kl_loss = F.gaussian_kl_divergence(mu_z, ln_var_z) / batch_size 267 | return dec_loss, kl_loss 268 | 269 | def pretrain_step_vrae(self, x_input, word_drop_ratio=0.0, train=True): 270 | """ 271 | Maximum likelihood Estimation 272 | 273 | :param x_input: 274 | :return: loss 275 | """ 276 | batch_size = len(x_input) 277 | _, mu_z, ln_var_z = self.encoder.encode(x_input, train) 278 | self.reset_state() 279 | 280 | if self.latent_dim: 281 | z = F.gaussian(mu_z, ln_var_z) 282 | else: 283 | self.lstm1.h = F.gaussian(mu_z, ln_var_z) 284 | z = None 285 | 286 | accum_loss = 0 287 | for i in range(self.sequence_length): 288 | if i == 0: 289 | x = chainer.Variable(self.xp.asanyarray([self.start_token] * batch_size, 'int32'), volatile=not train) 290 | else: 291 | if np.random.random() < word_drop_ratio and train: 292 | x = chainer.Variable(self.xp.asanyarray([self.start_token] * batch_size, 'int32'), volatile=not train) 293 | else: 294 | x = chainer.Variable(self.xp.asanyarray(x_input[:, i - 1], 'int32'), volatile=not train) 295 | 296 | scores = self.decode_one_step(x, z=z) 297 | loss = F.softmax_cross_entropy(scores, chainer.Variable(self.xp.asanyarray(x_input[:, i], 'int32'), volatile=not train)) 298 | accum_loss += loss 299 | 300 | dec_loss = accum_loss 301 | kl_loss = F.gaussian_kl_divergence(mu_z, ln_var_z) / batch_size 302 | return dec_loss, kl_loss 303 | 304 | def pretrain_step_autoencoder(self, x_input): 305 | """ 306 | Maximum likelihood Estimation 307 | 308 | :param x_input: 309 | :return: loss 310 | """ 311 | 312 | h, _, _ = self.encoder.encode(x_input) 313 | 314 | self.reset_state() 315 | batch_size = len(x_input) 316 | accum_loss = 0 317 | self.lstm1.h = h 318 | 319 | for i in range(self.sequence_length): 320 | if i == 0: 321 | x = chainer.Variable(self.xp.asanyarray([self.start_token] * batch_size, 'int32')) 322 | else: 323 | x = chainer.Variable(self.xp.asanyarray(x_input[:, i - 1], 'int32')) 324 | 325 | scores = self.decode_one_step(x) 326 | loss = F.softmax_cross_entropy(scores, chainer.Variable(self.xp.asanyarray(x_input[:, i], 'int32'))) 327 | accum_loss += loss 328 | 329 | return accum_loss / self.sequence_length 330 | 331 | def pretrain_step(self, x_input, tag=None): 332 | """ 333 | Maximum likelihood Estimation 334 | 335 | :param x_input: 336 | :return: loss 337 | """ 338 | self.reset_state() 339 | batch_size = len(x_input) 340 | accum_loss = 0 341 | if tag is not None: 342 | self.lstm1.h = self.tag_embed(chainer.Variable(self.xp.array(tag, 'int32'))) 343 | 344 | for i in range(self.sequence_length): 345 | if i == 0: 346 | if self.free_pretrain: 347 | x = chainer.Variable(self.xp.asanyarray(np.random.normal(scale=0.1, size=(batch_size, self.emb_dim)), 'float32')) 348 | else: 349 | x = chainer.Variable(self.xp.asanyarray([self.start_token] * batch_size, 'int32')) 350 | else: 351 | x = chainer.Variable(self.xp.asanyarray(x_input[:, i - 1], 'int32')) 352 | 353 | scores = self.decode_one_step(x) 354 | loss = F.softmax_cross_entropy(scores, chainer.Variable(self.xp.asanyarray(x_input[:, i], 'int32'))) 355 | accum_loss += loss 356 | 357 | return accum_loss # / self.sequence_length 358 | 359 | def reinforcement_step(self, x_input, rewards, g_steps, tag=None, random_input=False): 360 | """ 361 | :param x_input: (batch_size, seq_length) 362 | :param rewards: (batch_size, seq_length) 363 | :param g_steps: g_steps 364 | :return: 365 | """ 366 | self.reset_state() 367 | batch_size = len(x_input) 368 | accum_loss = 0 369 | if tag is not None: 370 | self.lstm1.h = self.tag_embed(chainer.Variable(self.xp.array(tag, 'int32'))) 371 | 372 | for j in range(self.sequence_length): 373 | if j == 0: 374 | if random_input: 375 | x = chainer.Variable(self.xp.asanyarray(self.x0, 'float32')) 376 | else: 377 | x = chainer.Variable(self.xp.asanyarray([self.start_token] * batch_size, 'int32')) 378 | else: 379 | x = chainer.Variable(self.xp.asanyarray(x_input[:, j - 1], 'int32')) 380 | 381 | scores = self.decode_one_step(x) 382 | log_prob = F.log_softmax(scores) # (batch_size, vocab_size) 383 | loss = 0 384 | for i in range(batch_size): 385 | loss += log_prob[i, x_input[i, j]] * rewards[i, j] 386 | accum_loss += loss 387 | 388 | return -accum_loss / g_steps 389 | 390 | def get_rewards(self, samples, dis, rollout_num=16, tag=None, pool=None, gpu=0): 391 | """ 392 | get reward from generated sample for ROLLOUT 393 | 394 | :param samples: generated_sample (batch, seq_length) 395 | :param dis: discriminator 396 | :param pool: multiprocess.Pool 397 | :param rollout_num: num of roll out 398 | :param gpu: gpu_id 399 | 400 | :return: (batch, seq_length) rewards[i,j] means rewards of a_{j-1} of batch i 401 | """ 402 | 403 | batch_size = len(samples) 404 | 405 | if pool: 406 | dis.to_cpu() 407 | rewards = pool.map(self.roll_out, [(samples, given, dis, rollout_num, gpu) for given in range(1, self.sequence_length)]) 408 | dis.to_gpu() 409 | rewards.append(dis.get_reward(samples)) 410 | return np.array(rewards).T 411 | else: 412 | reward_mat = np.zeros((batch_size, self.sequence_length), 'float32') 413 | for given in range(1, self.sequence_length): 414 | if tag is not None: 415 | rewards = self.roll_out((samples, given, dis, rollout_num, gpu, tag)) 416 | else: 417 | rewards = self.roll_out((samples, given, dis, rollout_num)) 418 | 419 | reward_mat[:, given - 1] = rewards 420 | 421 | reward_mat[:, 19] = dis.get_reward(samples) 422 | return reward_mat 423 | 424 | def roll_out(self, args): 425 | """ 426 | compute expected rewards 427 | 428 | :param samples: generated_sample 429 | :param given: use x_0 ~ x_given as generated (state) 430 | :param dis: discriminator 431 | :param pool: multiprocess.Pool 432 | :param rollout_num: num of roll out 433 | 434 | :return: rewards (batch_size) 435 | """ 436 | tag = None 437 | if len(args) == 4: 438 | samples, given, dis, rollout_num = args 439 | elif len(args) == 5: 440 | samples, given, dis, rollout_num, gpu = args 441 | cuda.get_device(gpu).use() 442 | dis.to_gpu() 443 | self.to_gpu() 444 | elif len(args) == 6: 445 | samples, given, dis, rollout_num, gpu, tag = args 446 | else: 447 | raise AssertionError('undesired argument') 448 | 449 | batch_size = len(samples) 450 | self.reset_state() 451 | 452 | if tag is not None: 453 | self.lstm1.h = self.tag_embed(chainer.Variable(self.xp.array(tag, 'int32'))) 454 | 455 | gen_x = np.zeros((batch_size, self.sequence_length), 'int32') 456 | 457 | x = chainer.Variable(self.xp.asanyarray([self.start_token] * batch_size, 'int32'), volatile=True) 458 | self.decode_one_step(x, False) 459 | for i in range(given): 460 | gen_x[:, i] = samples[:, i] 461 | x = chainer.Variable(self.xp.asanyarray(samples[:, i], 'int32'), volatile=True) 462 | scores = self.decode_one_step(x, False) 463 | 464 | scores_ = scores 465 | self.save_state() 466 | 467 | rewards = [] 468 | for _ in range(rollout_num): 469 | self.set_state() 470 | scores = chainer.Variable(scores_.data.copy(), volatile=True) 471 | for i in range(given, self.sequence_length): 472 | 473 | pred = F.softmax(scores) 474 | pred = cuda.to_cpu(pred.data) 475 | 476 | generated = [np.random.choice(self.vocab_size, p=pred[j]) for j in range(batch_size)] 477 | 478 | # pred = cuda.to_cpu(pred.data) - np.finfo(np.float32).epsneg 479 | # generated = [] 480 | # for j in range(batch_size): 481 | # histogram = np.random.multinomial(1, pred[j]) 482 | # generated.append(int(np.nonzero(histogram)[0])) 483 | 484 | gen_x[:, i] = generated 485 | x = chainer.Variable(self.xp.asanyarray(generated, 'int32'), volatile=True) 486 | scores = self.decode_one_step(x, False) 487 | 488 | rewards.append(dis.get_reward(gen_x)) 489 | 490 | return np.mean(rewards, axis=0) 491 | 492 | def target_loss(self, target_lstm, generated_num, batch_size, sess): 493 | 494 | # Generated Samples 495 | supervised_g_losses = [] 496 | 497 | for _ in range(int(generated_num / batch_size)): 498 | gen = self.generate(batch_size) 499 | g_loss = sess.run(target_lstm.pretrain_loss, {target_lstm.x: gen}) 500 | supervised_g_losses.append(g_loss) 501 | 502 | return np.mean(supervised_g_losses) 503 | 504 | 505 | 506 | 507 | -------------------------------------------------------------------------------- /models/text_cnn.py: -------------------------------------------------------------------------------- 1 | 2 | import chainer 3 | import chainer.functions as F 4 | import chainer.links as L 5 | import numpy as np 6 | 7 | 8 | class TextCNN(chainer.Chain): 9 | """ 10 | A CNN for text classification. 11 | Uses an embedding layer, followed by a convolutional, max-pooling and softmax layer. 12 | """ 13 | 14 | def __init__(self, num_classes, vocab_size, 15 | embedding_size, filter_sizes, num_filters): 16 | 17 | self.num_emb = vocab_size 18 | self.emb_dim = embedding_size 19 | self.filter_sizes = filter_sizes 20 | self.num_filters = num_filters 21 | self.num_classes = num_classes 22 | self.temperature = 1.0 23 | 24 | layers = dict() 25 | # embedding 26 | layers['embed'] = L.EmbedID(self.num_emb, self.emb_dim) 27 | # conv-filter 28 | layers.update({ 29 | 'conv-{}'.format(i): L.Convolution2D(1, num_filter, (filter_size, self.emb_dim)) 30 | for i, (filter_size, num_filter) in enumerate(zip(self.filter_sizes, self.num_filters)) 31 | }) 32 | # highway-architecture 33 | layers['highway_out'] = L.Linear(sum(self.num_filters), sum(self.num_filters), nobias=True) 34 | layers['highway_gate'] = L.Linear(sum(self.num_filters), sum(self.num_filters), nobias=True) 35 | # output-layer 36 | layers['out'] = L.Linear(sum(self.num_filters), num_classes) 37 | 38 | super(TextCNN, self).__init__(**layers) 39 | 40 | def forward(self, x_input, ratio=0.5, train=True): 41 | 42 | try: 43 | batch_size, seq_length = x_input.shape 44 | except: 45 | batch_size = len(x_input) 46 | seq_length = len(x_input[0]) 47 | 48 | x = chainer.Variable(self.xp.asarray(x_input, 'int32')) 49 | 50 | # embedding 51 | h1 = self.embed(x)[:, None, :, :] 52 | 53 | # conv-pooling 54 | pooled = [] 55 | for i, (filter_size, num_filter) in enumerate(zip(self.filter_sizes, self.num_filters)): 56 | h2 = F.max_pooling_2d(F.relu(getattr(self, 'conv-{}'.format(i))(h1)), ksize=(seq_length - filter_size + 1, 1), stride=1) 57 | pooled.append(F.reshape(h2, (batch_size, -1))) 58 | h3 = F.concat(pooled) 59 | 60 | # highway network 61 | t = F.sigmoid(self.highway_gate(h3)) 62 | h4 = t * F.relu(self.highway_out(h3)) + (1 - t) * h3 63 | h5 = F.dropout(h4, ratio=ratio, train=train) 64 | 65 | return self.out(h5) 66 | 67 | def get_reward(self, x_input): 68 | pred = F.softmax(self.forward(x_input, train=False)).data 69 | return np.array([float(item[1]) for item in pred]) 70 | 71 | def __call__(self, x_input, t, dis_dropout_keep_prob=0.5, train=True): 72 | 73 | y = self.forward(x_input, ratio=1-dis_dropout_keep_prob, train=train) 74 | t = chainer.Variable(self.xp.asarray(t, 'int32')) 75 | 76 | return F.softmax_cross_entropy(y, t), F.accuracy(y, t) 77 | -------------------------------------------------------------------------------- /nico_comment/dis_profiler.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 3, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import argparse\n", 12 | "import os\n", 13 | "import pickle\n", 14 | "import random\n", 15 | "import time\n", 16 | "import sys\n", 17 | "import datetime\n", 18 | "import copy\n", 19 | "import numpy as np\n", 20 | "import chainer\n", 21 | "from chainer import optimizers\n", 22 | "from chainer import cuda\n", 23 | "from chainer import serializers\n", 24 | "import tensorflow as tf\n", 25 | "import multiprocessing as mp\n", 26 | "\n", 27 | "sys.path.append('../../chainer-SeqGAN/')\n", 28 | "\n", 29 | "from models import SeqGAN, TextCNN, SeqEncoder\n", 30 | "from optimizer_hook import NamedWeightDecay\n", 31 | "\n", 32 | "parser = argparse.ArgumentParser()\n", 33 | "parser.add_argument(\"--out\", default='')\n", 34 | "parser.add_argument(\"--gen\", default='')\n", 35 | "parser.add_argument(\"--dis\", default='')\n", 36 | "parser.add_argument('--gpu', '-g', type=int, default=0)\n", 37 | "parser.add_argument('--parallel', '-p', default=0, type=int)\n", 38 | "\n", 39 | "# Generator Hyper-parameters\n", 40 | "parser.add_argument(\"--gen_emb_dim\", type=int, default=128)\n", 41 | "parser.add_argument(\"--gen_hidden_dim\", type=int, default=128)\n", 42 | "parser.add_argument(\"--gen_grad_clip\", type=int, default=5)\n", 43 | "parser.add_argument(\"--gen_lr\", type=float, default=1e-3)\n", 44 | "parser.add_argument(\"--num_lstm_layer\", type=int, default=1)\n", 45 | "parser.add_argument(\"--no-dropout\", dest='dropout', action='store_false', default=True)\n", 46 | "\n", 47 | "# Discriminator Hyper-parameters\n", 48 | "parser.add_argument(\"--dis_embedding_dim\", type=int, default=64)\n", 49 | "parser.add_argument(\"--dis_filter_sizes\", default=\"1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20\")\n", 50 | "parser.add_argument(\"--dis_num_filters\", default=\"100, 200, 200, 200, 200, 100, 100, 100, 100, 100, 160, 160\")\n", 51 | "parser.add_argument(\"--dis_dropout_keep_prob\", type=float, default=0.75)\n", 52 | "parser.add_argument(\"--dis_l2_reg_lambda\", type=float, default=0.2)\n", 53 | "parser.add_argument(\"--dis_lr\", type=float, default=1e-4)\n", 54 | "\n", 55 | "# Training Hyper-parameters\n", 56 | "parser.add_argument(\"--batch_size\", type=int, default=100)\n", 57 | "parser.add_argument(\"--total_epoch\", type=int, default=800)\n", 58 | "parser.add_argument(\"--gen_pretrain_epoch\", type=int, default=100)\n", 59 | "parser.add_argument(\"--dis_pretrain_epoch\", type=int, default=50)\n", 60 | "\n", 61 | "parser.add_argument(\"--g_steps\", type=int, default=1)\n", 62 | "parser.add_argument(\"--d_steps\", type=int, default=5)\n", 63 | "parser.add_argument(\"--K\", type=int, default=5)\n", 64 | "\n", 65 | "parser.add_argument(\"--rollout_update_ratio\", type=float, default=0.8)\n", 66 | "parser.add_argument(\"--sample_per_iter\", type=int, default=10000)\n", 67 | "\n", 68 | "parser.add_argument(\"--free-pretrain\", dest='free_pretrain', action='store_true', default=False)\n", 69 | "parser.add_argument(\"--ae-pretrain\", dest='ae_pretrain', action='store_true', default=False)\n", 70 | "\n", 71 | "args, _ = parser.parse_known_args()\n" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 4, 77 | "metadata": { 78 | "collapsed": true 79 | }, 80 | "outputs": [], 81 | "source": [ 82 | "# load nico-comment dataset loader\n", 83 | "with open('nico_comment_processed.dat', 'rb') as f:\n", 84 | " train_comment_data, test_comment_data, train_tag_data, test_tag_data, vocab, tag_id = pickle.load(f)\n", 85 | "\n", 86 | "train_num = len(train_comment_data)\n", 87 | "test_num = len(test_comment_data)\n", 88 | "vocab_size = 3000\n", 89 | "seq_length = 30\n", 90 | "start_token = 0\n", 91 | "\n", 92 | "def progress_report(count, start_time, batchsize):\n", 93 | " duration = time.time() - start_time\n", 94 | " throughput = count * batchsize / duration\n", 95 | " sys.stderr.write(\n", 96 | " '\\r{} updates ({} samples) time: {} ({:.2f} samples/sec)'.format(\n", 97 | " count, count * batchsize, str(datetime.timedelta(seconds=duration)).split('.')[0], throughput\n", 98 | " )\n", 99 | " )\n" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 7, 105 | "metadata": { 106 | "collapsed": false 107 | }, 108 | "outputs": [], 109 | "source": [ 110 | "generator = SeqGAN(vocab_size=vocab_size, emb_dim=args.gen_emb_dim, hidden_dim=args.gen_hidden_dim,\n", 111 | " sequence_length=seq_length, start_token=start_token, lstm_layer=1,\n", 112 | " dropout=args.dropout, free_pretrain=args.free_pretrain, encoder=None).to_gpu()\n", 113 | "\n", 114 | "serializers.load_hdf5('runs/first/models/gen_pretrain_20.model', generator)" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 2, 120 | "metadata": { 121 | "collapsed": false 122 | }, 123 | "outputs": [ 124 | { 125 | "ename": "NameError", 126 | "evalue": "name 'TextCNN' is not defined", 127 | "output_type": "error", 128 | "traceback": [ 129 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 130 | "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", 131 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m discriminator = TextCNN(num_classes=2, vocab_size=vocab_size, embedding_size=args.dis_embedding_dim,\n\u001b[0m\u001b[0;32m 2\u001b[0m \u001b[0mfilter_sizes\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mn\u001b[0m \u001b[1;32min\u001b[0m \u001b[0margs\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdis_filter_sizes\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m','\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[0mnum_filters\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mn\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mn\u001b[0m \u001b[1;32min\u001b[0m \u001b[0margs\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdis_num_filters\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m','\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m ).to_gpu()\n", 132 | "\u001b[1;31mNameError\u001b[0m: name 'TextCNN' is not defined" 133 | ] 134 | } 135 | ], 136 | "source": [ 137 | "discriminator = TextCNN(num_classes=2, vocab_size=vocab_size, embedding_size=args.dis_embedding_dim,\n", 138 | " filter_sizes=[int(n) for n in args.dis_filter_sizes.split(',')],\n", 139 | " num_filters=[int(n) for n in args.dis_num_filters.split(',')]\n", 140 | " ).to_gpu()" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 16, 146 | "metadata": { 147 | "collapsed": false 148 | }, 149 | "outputs": [ 150 | { 151 | "name": "stdout", 152 | "output_type": "stream", 153 | "text": [ 154 | "\n", 155 | "pre-trained test_loss 1.4349755005041758\n" 156 | ] 157 | } 158 | ], 159 | "source": [ 160 | "test_loss = []\n", 161 | "batch_size = 100\n", 162 | "perm = np.random.permutation(test_num)\n", 163 | "for i in range(0, test_num, batch_size):\n", 164 | " batch = test_comment_data[perm[i:i + batch_size]]\n", 165 | " g_loss = generator.pretrain_step(batch)\n", 166 | " test_loss.append(float(g_loss.data))\n", 167 | "print('\\npre-trained test_loss {}'.format(np.mean(test_loss)))\n", 168 | "test_count = args.gen_pretrain_epoch\n" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": 20, 174 | "metadata": { 175 | "collapsed": false 176 | }, 177 | "outputs": [], 178 | "source": [ 179 | "positive = train_comment_data[np.random.permutation(train_num)[:args.sample_per_iter]]\n" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 21, 185 | "metadata": { 186 | "collapsed": false 187 | }, 188 | "outputs": [ 189 | { 190 | "data": { 191 | "text/plain": [ 192 | "10000" 193 | ] 194 | }, 195 | "execution_count": 21, 196 | "metadata": {}, 197 | "output_type": "execute_result" 198 | } 199 | ], 200 | "source": [ 201 | "len(positive)" 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "execution_count": 22, 207 | "metadata": { 208 | "collapsed": false 209 | }, 210 | "outputs": [ 211 | { 212 | "name": "stdout", 213 | "output_type": "stream", 214 | "text": [ 215 | " " 216 | ] 217 | } 218 | ], 219 | "source": [ 220 | "%prun negative = generator.generate(args.sample_per_iter)\n" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": 24, 226 | "metadata": { 227 | "collapsed": false 228 | }, 229 | "outputs": [ 230 | { 231 | "data": { 232 | "text/plain": [ 233 | "(10000, 30)" 234 | ] 235 | }, 236 | "execution_count": 24, 237 | "metadata": {}, 238 | "output_type": "execute_result" 239 | } 240 | ], 241 | "source": [ 242 | "positive.shape" 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": 25, 248 | "metadata": { 249 | "collapsed": false 250 | }, 251 | "outputs": [ 252 | { 253 | "data": { 254 | "text/plain": [ 255 | "array([2148, 928, 362, 1158, 359, 358, 351, 347, 389, 1340, 1751,\n", 256 | " 358, 331, 358, 391, 326, 1, 2999, 2999, 2999, 2999, 2999,\n", 257 | " 2999, 2999, 2999, 2999, 2999, 2999, 2999, 2999], dtype=int32)" 258 | ] 259 | }, 260 | "execution_count": 25, 261 | "metadata": {}, 262 | "output_type": "execute_result" 263 | } 264 | ], 265 | "source": [ 266 | "positive[0]" 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": null, 272 | "metadata": { 273 | "collapsed": true 274 | }, 275 | "outputs": [], 276 | "source": [] 277 | } 278 | ], 279 | "metadata": { 280 | "anaconda-cloud": {}, 281 | "kernelspec": { 282 | "display_name": "Python [Root]", 283 | "language": "python", 284 | "name": "Python [Root]" 285 | }, 286 | "language_info": { 287 | "codemirror_mode": { 288 | "name": "ipython", 289 | "version": 3 290 | }, 291 | "file_extension": ".py", 292 | "mimetype": "text/x-python", 293 | "name": "python", 294 | "nbconvert_exporter": "python", 295 | "pygments_lexer": "ipython3", 296 | "version": "3.5.1" 297 | } 298 | }, 299 | "nbformat": 4, 300 | "nbformat_minor": 0 301 | } 302 | -------------------------------------------------------------------------------- /nico_comment/generate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function 3 | 4 | import argparse 5 | import chainer.serializers 6 | import os 7 | import sys 8 | import pickle 9 | import numpy as np 10 | from model import SeqGAN 11 | import time 12 | import datetime 13 | import multiprocessing as mp 14 | 15 | pool = mp.Pool() 16 | 17 | generator = SeqGAN(vocab_size=3000, emb_dim=128, hidden_dim=128, 18 | sequence_length=40, start_token=0, lstm_layer=2 19 | ).to_gpu() 20 | 21 | batch_size = 10000 22 | 23 | 24 | def progress_report(count, start_time, batch_size): 25 | duration = time.time() - start_time 26 | throughput = count * batch_size / duration 27 | sys.stderr.write( 28 | '\rtrain {} updates ({} samples) time: {} ({:.2f} samples/sec)' 29 | .format(count, count * batch_size, 30 | str(datetime.timedelta(seconds=duration)).split('.')[0], throughput)) 31 | 32 | negative = [] 33 | 34 | # pool=None 35 | st = time.time() 36 | for x in range(30000 // batch_size): 37 | negative.append(generator.generate(batch_size)) 38 | progress_report(x, st, batch_size) 39 | t = time.time() 40 | print() 41 | print(t - st) 42 | for x in range(30000 // batch_size): 43 | negative.append(generator.generate(batch_size, pool)) 44 | progress_report(x, t, batch_size) 45 | print(time.time() - t) 46 | -------------------------------------------------------------------------------- /nico_comment/run_sequence_gan.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import pickle 4 | import random 5 | import time 6 | import sys 7 | import datetime 8 | import copy 9 | import numpy as np 10 | import chainer 11 | from chainer import optimizers 12 | from chainer import cuda 13 | from chainer import serializers 14 | import tensorflow as tf 15 | import multiprocessing as mp 16 | 17 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 18 | 19 | from models import SeqGAN, TextCNN, SeqEncoder 20 | from optimizer_hook import NamedWeightDecay 21 | 22 | os.environ['PATH'] += ':/usr/local/cuda/bin' 23 | 24 | parser = argparse.ArgumentParser() 25 | parser.add_argument("--out", default='') 26 | parser.add_argument("--gen", default='') 27 | parser.add_argument("--dis", default='') 28 | parser.add_argument('--gpu', '-g', type=int, default=0) 29 | parser.add_argument('--parallel', '-p', default=0, type=int) 30 | 31 | # Generator Hyper-parameters 32 | parser.add_argument("--gen_emb_dim", type=int, default=128) 33 | parser.add_argument("--gen_hidden_dim", type=int, default=128) 34 | parser.add_argument("--gen_grad_clip", type=int, default=5) 35 | parser.add_argument("--gen_lr", type=float, default=1e-3) 36 | parser.add_argument("--num_lstm_layer", type=int, default=1) 37 | parser.add_argument("--no-dropout", dest='dropout', action='store_false', default=True) 38 | 39 | # Discriminator Hyper-parameters 40 | parser.add_argument("--dis_embedding_dim", type=int, default=64) 41 | parser.add_argument("--dis_filter_sizes", default="1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20") 42 | parser.add_argument("--dis_num_filters", default="100, 200, 200, 200, 200, 100, 100, 100, 100, 100, 160, 160") 43 | parser.add_argument("--dis_dropout_keep_prob", type=float, default=0.75) 44 | parser.add_argument("--dis_l2_reg_lambda", type=float, default=0.2) 45 | parser.add_argument("--dis_lr", type=float, default=1e-4) 46 | 47 | # Training Hyper-parameters 48 | parser.add_argument("--batch_size", type=int, default=100) 49 | parser.add_argument("--total_epoch", type=int, default=800) 50 | parser.add_argument("--gen_pretrain_epoch", type=int, default=100) 51 | parser.add_argument("--dis_pretrain_epoch", type=int, default=50) 52 | 53 | parser.add_argument("--g_steps", type=int, default=1) 54 | parser.add_argument("--d_steps", type=int, default=3) 55 | parser.add_argument("--K", type=int, default=3) 56 | 57 | parser.add_argument("--rollout_update_ratio", type=float, default=0.8) 58 | parser.add_argument("--sample_per_iter", type=int, default=10000) 59 | 60 | parser.add_argument("--free-pretrain", dest='free_pretrain', action='store_true', default=False) 61 | parser.add_argument("--ae-pretrain", dest='ae_pretrain', action='store_true', default=False) 62 | 63 | args = parser.parse_args() 64 | 65 | # multiprocess worker 66 | if args.parallel > 0: 67 | pool = mp.Pool(16) 68 | else: 69 | pool = None 70 | 71 | batch_size = args.batch_size 72 | assert args.sample_per_iter % batch_size == 0 73 | 74 | out = datetime.datetime.now().strftime('%m%d') 75 | if args.out: 76 | out += '_'+args.out 77 | out_dir = os.path.abspath(os.path.join(os.path.curdir, "runs", out)) 78 | os.makedirs(os.path.join(out_dir, 'models'), exist_ok=True) 79 | 80 | with open(os.path.join(out_dir, 'setting.txt'), 'w') as f: 81 | for k, v in args._get_kwargs(): 82 | print('{} = {}'.format(k, v)) 83 | f.write('{} = {}\n'.format(k, v)) 84 | 85 | cuda.get_device(args.gpu).use() 86 | 87 | SEED = 88 88 | random.seed(SEED) 89 | np.random.seed(SEED) 90 | 91 | # load nico-comment dataset loader 92 | with open('nico_comment_processed3.dat', 'rb') as f: 93 | train_comment_data, test_comment_data, train_tag_data, test_tag_data, vocab, tag_id = pickle.load(f) 94 | 95 | train_num = len(train_comment_data) 96 | test_num = len(test_comment_data) 97 | vocab_size = 3000 98 | seq_length = 30 99 | start_token = 0 100 | 101 | if args.ae_pretrain: 102 | encoder = SeqEncoder(vocab_size=vocab_size, emb_dim=args.gen_emb_dim, hidden_dim=args.gen_hidden_dim, 103 | latent_dim=args.latent_dim, sequence_length=seq_length) 104 | else: 105 | encoder = None 106 | 107 | # generator 108 | generator = SeqGAN(vocab_size=vocab_size, emb_dim=args.gen_emb_dim, hidden_dim=args.gen_hidden_dim, 109 | sequence_length=seq_length, start_token=start_token, lstm_layer=args.num_lstm_layer, 110 | dropout=args.dropout, free_pretrain=args.free_pretrain, encoder=encoder, tag_dim=len(tag_id)).to_gpu() 111 | if args.gen: 112 | serializers.load_hdf5(args.gen, generator) 113 | 114 | # discriminator 115 | discriminator = TextCNN(num_classes=2, vocab_size=vocab_size, embedding_size=args.dis_embedding_dim, 116 | filter_sizes=[int(n) for n in args.dis_filter_sizes.split(',')], 117 | num_filters=[int(n) for n in args.dis_num_filters.split(',')] 118 | ).to_gpu() 119 | if args.dis: 120 | serializers.load_hdf5(args.dis, discriminator) 121 | 122 | # set optimizer 123 | if args.ae_pretrain: 124 | enc_optimizer = optimizers.Adam(alpha=args.gen_lr) 125 | enc_optimizer.setup(encoder) 126 | enc_optimizer.add_hook(chainer.optimizer.GradientClipping(args.gen_grad_clip)) 127 | 128 | gen_optimizer = optimizers.Adam(alpha=args.gen_lr) 129 | gen_optimizer.setup(generator) 130 | gen_optimizer.add_hook(chainer.optimizer.GradientClipping(args.gen_grad_clip)) 131 | 132 | dis_optimizer = optimizers.Adam(alpha=args.dis_lr) 133 | dis_optimizer.setup(discriminator) 134 | dis_optimizer.add_hook(NamedWeightDecay(args.dis_l2_reg_lambda, '/out/')) 135 | 136 | # summaries 137 | sess = tf.Session() 138 | sess.run(tf.initialize_all_variables()) 139 | 140 | summary_dir = os.path.join(out_dir, "summaries") 141 | 142 | loss_ = tf.placeholder(tf.float32) 143 | train_loss_summary = tf.scalar_summary('train_loss', loss_) 144 | test_loss_summary = tf.scalar_summary('test_loss', loss_) 145 | dis_loss_summary = tf.scalar_summary('dis_loss', loss_) 146 | dis_acc_summary = tf.scalar_summary('dis_acc', loss_) 147 | 148 | summary_writer = tf.train.SummaryWriter(summary_dir, sess.graph) 149 | 150 | dis_train_count = 0 151 | gen_train_count = 0 152 | test_count = 0 153 | 154 | with open(os.path.join(out_dir, "generated_sample_pretrain.txt"), 'w') as f: 155 | f.write('') 156 | 157 | with open(os.path.join(out_dir, "generated_sample.txt"), 'w') as f: 158 | f.write('') 159 | 160 | 161 | def progress_report(count, start_time, batchsize): 162 | duration = time.time() - start_time 163 | throughput = count * batchsize / duration 164 | sys.stderr.write( 165 | '\r{} updates ({} samples) time: {} ({:.2f} samples/sec)'.format( 166 | count, count * batchsize, str(datetime.timedelta(seconds=duration)).split('.')[0], throughput 167 | ) 168 | ) 169 | 170 | 171 | if not args.gen: 172 | print('Start pre-training generator...') 173 | start = time.time() 174 | batch_size *= 20 175 | for epoch in range(args.gen_pretrain_epoch): 176 | 177 | # pre-train 178 | pre_train_loss = [] 179 | perm = np.random.permutation(train_num) 180 | for i in range(0, train_num, batch_size): 181 | batch = train_comment_data[perm[i:i+batch_size]] 182 | tag_batch = train_tag_data[perm[i:i + batch_size]] 183 | 184 | if args.ae_pretrain: 185 | g_loss = generator.pretrain_step_autoencoder(batch) 186 | enc_optimizer.zero_grads() 187 | gen_optimizer.zero_grads() 188 | g_loss.backward() 189 | enc_optimizer.update() 190 | gen_optimizer.update() 191 | pre_train_loss.append(float(g_loss.data)) 192 | else: 193 | g_loss = generator.pretrain_step(batch, tag_batch) 194 | gen_optimizer.zero_grads() 195 | g_loss.backward() 196 | gen_optimizer.update() 197 | pre_train_loss.append(float(g_loss.data)) 198 | 199 | # progress report 200 | gen_train_count += 1 201 | progress_report(gen_train_count, start, batch_size) 202 | 203 | # test 204 | test_loss = [] 205 | perm = np.random.permutation(test_num) 206 | 207 | for i in range(0, test_num, batch_size): 208 | batch = test_comment_data[perm[i:i + batch_size]] 209 | tag_batch = test_tag_data[perm[i:i + batch_size]] 210 | 211 | g_loss = generator.pretrain_step(batch, tag_batch) 212 | test_loss.append(float(g_loss.data)) 213 | test_count += 1 214 | 215 | print('\npre-train epoch {} train_loss {} test_loss {}'.format(epoch, np.mean(pre_train_loss), 216 | np.mean(test_loss))) 217 | summary = sess.run(train_loss_summary, feed_dict={loss_: np.mean(pre_train_loss)}) 218 | summary_writer.add_summary(summary, test_count) 219 | summary = sess.run(test_loss_summary, feed_dict={loss_: np.mean(test_loss)}) 220 | summary_writer.add_summary(summary, test_count) 221 | samples = generator.generate(10, train=False) 222 | with open(os.path.join(out_dir, "generated_sample_pretrain.txt"), 'a', encoding='utf-8') as f: 223 | f.write('\npre-train epoch {} train_loss {} test_loss {} \n'.format(epoch, np.mean(pre_train_loss), 224 | np.mean(test_loss))) 225 | for x in samples: 226 | f.write(''.join([vocab[w] for w in x]) + '\n') 227 | 228 | if epoch % 1 == 0: 229 | serializers.save_hdf5(os.path.join(out_dir, "models", "gen_pretrain_{}.model".format(epoch)), generator) 230 | batch_size /= 20 231 | else: 232 | # test 233 | test_loss = [] 234 | perm = np.random.permutation(test_num) 235 | for i in range(0, test_num, batch_size): 236 | batch = test_comment_data[perm[i:i + batch_size]] 237 | g_loss = generator.pretrain_step(batch) 238 | test_loss.append(float(g_loss.data)) 239 | print('\npre-trained test_loss {}'.format(np.mean(test_loss))) 240 | test_count = args.gen_pretrain_epoch 241 | summary = sess.run(test_loss_summary, feed_dict={loss_: np.mean(test_loss)}) 242 | summary_writer.add_summary(summary, test_count) 243 | 244 | 245 | # discriminator pre-train 246 | if not args.dis: 247 | train_count = 0 248 | start = time.time() 249 | print('Start pre-training discriminator...') 250 | 251 | for epoch in range(args.dis_pretrain_epoch): 252 | 253 | tag_batch = np.random.choice(train_tag_data, args.sample_per_iter) 254 | negative = generator.generate_use_tag(tag_batch, train=True) 255 | 256 | for k in range(args.K): 257 | 258 | positive = train_comment_data[np.random.permutation(train_num)[:args.sample_per_iter]] 259 | print(positive.shape, negative.shape) 260 | x = np.vstack([positive, negative]) 261 | y = np.array([1] * args.sample_per_iter + [0] * args.sample_per_iter) 262 | sum_train_loss = [] 263 | sum_train_accuracy = [] 264 | perm = np.random.permutation(len(y)) 265 | 266 | for i in range(0, len(y), batch_size): 267 | x_batch = x[perm[i:i + batch_size]] 268 | y_batch = y[perm[i:i + batch_size]] 269 | loss, acc = discriminator(x_batch, y_batch, args.dis_dropout_keep_prob) 270 | dis_optimizer.zero_grads() 271 | loss.backward() 272 | dis_optimizer.update() 273 | sum_train_loss.append(float(loss.data)) 274 | sum_train_accuracy.append(float(acc.data)) 275 | 276 | train_count += 1 277 | progress_report(train_count, start, batch_size) 278 | 279 | print('\ndis-train epoch ', epoch, 'train_loss ', np.mean(sum_train_loss), 'train_acc ', 280 | np.mean(sum_train_accuracy)) 281 | dis_train_count += 1 282 | summary = sess.run(dis_loss_summary, feed_dict={loss_: np.mean(sum_train_loss)}) 283 | summary_writer.add_summary(summary, dis_train_count) 284 | summary = sess.run(dis_acc_summary, feed_dict={loss_: np.mean(sum_train_accuracy)}) 285 | summary_writer.add_summary(summary, dis_train_count) 286 | 287 | serializers.save_hdf5(os.path.join(out_dir, "models", "dis_pretrain.model"), discriminator) 288 | 289 | else: 290 | negative = generator.generate(1000) 291 | positive = train_comment_data[np.random.permutation(train_num)[:1000]] 292 | x = np.vstack([positive, negative]) 293 | y = np.array([1] * 1000 + [0] * 1000) 294 | sum_train_loss = [] 295 | sum_train_accuracy = [] 296 | perm = np.random.permutation(len(y)) 297 | for i in range(0, len(y), batch_size): 298 | x_batch = x[perm[i:i + batch_size]] 299 | y_batch = y[perm[i:i + batch_size]] 300 | loss, acc = discriminator(x_batch, y_batch, args.dis_dropout_keep_prob) 301 | dis_optimizer.zero_grads() 302 | loss.backward() 303 | dis_optimizer.update() 304 | sum_train_loss.append(float(loss.data)) 305 | sum_train_accuracy.append(float(acc.data)) 306 | 307 | print('\ndis-pretrain train_loss ', np.mean(sum_train_loss), 'train_acc ', np.mean(sum_train_accuracy)) 308 | dis_train_count = args.dis_pretrain_epoch 309 | summary = sess.run(dis_loss_summary, feed_dict={loss_: np.mean(sum_train_loss)}) 310 | summary_writer.add_summary(summary, dis_train_count) 311 | summary = sess.run(dis_acc_summary, feed_dict={loss_: np.mean(sum_train_accuracy)}) 312 | summary_writer.add_summary(summary, dis_train_count) 313 | 314 | gen_optimizer = optimizers.Adam(alpha=args.gen_lr) 315 | gen_optimizer.setup(generator) 316 | gen_optimizer.add_hook(chainer.optimizer.GradientClipping(args.gen_grad_clip)) 317 | 318 | # roll out generator 319 | rollout_generator = copy.deepcopy(generator).to_cpu() 320 | rollout_params = np.asanyarray(tuple(param.data for param in rollout_generator.params())) 321 | 322 | print('#########################################################################\n') 323 | print('Start Reinforcement Training ...') 324 | 325 | start = time.time() 326 | for epoch in range(1, args.total_epoch): 327 | 328 | print('total epoch ', epoch) 329 | tmp = time.time() 330 | # g-step 331 | mean_time = 0 332 | for step in range(args.g_steps): 333 | tag_batch = np.random.choice(train_tag_data, args.sample_per_iter) 334 | negative = generator.generate_use_tag(tag_batch, train=True) 335 | rewards = rollout_generator.get_rewards(samples, discriminator, tag=tag_batch, pool=pool, gpu=args.gpu) 336 | loss = generator.reinforcement_step(samples, rewards, tag=tag_batch, g_steps=args.g_steps) 337 | gen_optimizer.zero_grads() 338 | loss.backward() 339 | gen_optimizer.update() 340 | 341 | duration = time.time() - start 342 | step_time = time.time() - tmp 343 | mean_time += step_time 344 | tmp = time.time() 345 | sys.stderr.write('\rreinforce step {}/{} time: {} ({:.2f} sec/step)'.format( 346 | step + 1, args.g_steps, str(datetime.timedelta(seconds=duration)).split('.')[0], step_time)) 347 | else: 348 | print('\rreinforce step {}/{} time: {} ({:.2f} sec/step)'.format( 349 | step + 1, args.g_steps, str(datetime.timedelta(seconds=duration)).split('.')[0], mean_time/args.g_steps)) 350 | 351 | # update rollout generator 352 | for i, param in enumerate(generator.params()): 353 | rollout_params[i] += args.rollout_update_ratio * (cuda.to_cpu(param.data) - rollout_params[i]) 354 | 355 | # d-step 356 | mean_time = 0 357 | for step in range(args.d_steps): 358 | 359 | # negative = np.vstack([generator.generate(batch_size, pool=pool) for x in range(args.sample_per_iter // batch_size)]) 360 | tag_batch = np.random.choice(train_tag_data, batch_size) 361 | negative = generator.generate(args.sample_per_iter, tag_batch) 362 | 363 | for k in range(args.K): 364 | positive = train_comment_data[np.random.permutation(train_num)[:args.sample_per_iter]] 365 | 366 | x = np.vstack([positive, negative]) 367 | y = np.array([1] * args.sample_per_iter + [0] * args.sample_per_iter) 368 | sum_train_loss = [] 369 | sum_train_accuracy = [] 370 | perm = np.random.permutation(len(y)) 371 | 372 | for i in range(0, len(y), batch_size): 373 | x_batch = x[perm[i:i + batch_size]] 374 | y_batch = y[perm[i:i + batch_size]] 375 | loss, acc = discriminator(x_batch, y_batch, args.dis_dropout_keep_prob) 376 | dis_optimizer.zero_grads() 377 | loss.backward() 378 | dis_optimizer.update() 379 | sum_train_loss.append(float(loss.data)) 380 | sum_train_accuracy.append(float(acc.data)) 381 | 382 | dis_train_count += 1 383 | summary = sess.run(dis_loss_summary, feed_dict={loss_: np.mean(sum_train_loss)}) 384 | summary_writer.add_summary(summary, dis_train_count) 385 | summary = sess.run(dis_acc_summary, feed_dict={loss_: np.mean(sum_train_accuracy)}) 386 | summary_writer.add_summary(summary, dis_train_count) 387 | 388 | duration = time.time() - start 389 | step_time = time.time() - tmp 390 | mean_time += step_time 391 | tmp = time.time() 392 | sys.stderr.write('\rdis-train step {}/{} time: {} ({:.2f} sec/step)'.format( 393 | step + 1, args.d_steps, str(datetime.timedelta(seconds=duration)).split('.')[0], step_time)) 394 | else: 395 | print('\rdis-train step {}/{} time: {} ({:.2f} sec/step)'.format( 396 | step + 1, args.d_steps, str(datetime.timedelta(seconds=duration)).split('.')[0], mean_time / args.d_steps)) 397 | 398 | test_loss = [] 399 | perm = np.random.permutation(test_num) 400 | 401 | for i in range(0, test_num, batch_size): 402 | batch = test_comment_data[perm[i:i + batch_size]] 403 | g_loss = generator.pretrain_step(batch) 404 | test_loss.append(float(g_loss.data)) 405 | 406 | print('test_loss {}'.format(np.mean(test_loss))) 407 | test_count += 1 408 | summary = sess.run(test_loss_summary, feed_dict={loss_: np.mean(test_loss)}) 409 | summary_writer.add_summary(summary, test_count) 410 | samples = generator.generate(10, train=False) 411 | with open(os.path.join(out_dir, "generated_sample.txt"), 'a', encoding='utf-8') as f: 412 | f.write('\ntotal epoch {} test_loss {} \n'.format(epoch, np.mean(test_loss))) 413 | for x in samples: 414 | f.write(''.join([vocab[w] for w in x]) + '\n') 415 | 416 | if epoch % 1 == 0: 417 | serializers.save_hdf5(os.path.join(out_dir, "models/gen_{:03d}.model".format(epoch)), generator) 418 | serializers.save_hdf5(os.path.join(out_dir, "models/dis_{:03d}.model".format(epoch)), discriminator) 419 | -------------------------------------------------------------------------------- /nico_comment/summary_test.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import numpy as np 3 | import os 4 | from tensorflow.python.summary.event_accumulator import EventAccumulator 5 | 6 | 7 | def scalar2arrays(scalarEvents): 8 | """ 9 | converts scalarEvent to set of numpy.array 10 | """ 11 | wall_times = [] 12 | steps = [] 13 | values = [] 14 | 15 | for event in scalarEvents: 16 | wall_times.append(event.wall_time) 17 | steps.append(event.step) 18 | values.append(event.value) 19 | 20 | return np.array(wall_times), np.array(steps), np.array(values) 21 | 22 | 23 | sess = tf.Session() 24 | sess.run(tf.initialize_all_variables()) 25 | 26 | 27 | if __name__ == '__main__': 28 | 29 | for d in os.listdir('runs_vrae'): 30 | di = 'runs_vrae/' + d 31 | print(di) 32 | if os.path.isdir(di): 33 | summaries = di + '/summaries/' 34 | for f in os.listdir(summaries): 35 | accumulator = EventAccumulator(summaries + f) 36 | accumulator.Reload() # load event files 37 | try: 38 | _, steps, kl_values = scalar2arrays(accumulator.Scalars('test_loglikelihood')) 39 | os.system('mv {}{} old/'.format(summaries, f)) 40 | except: 41 | continue 42 | 43 | for d in os.listdir('runs_vrae'): 44 | di = 'runs_vrae/' + d 45 | print(di) 46 | if os.path.isdir(di): 47 | 48 | sess = tf.Session() 49 | sess.run(tf.initialize_all_variables()) 50 | 51 | summaries = di + '/summaries/' 52 | accumulator = EventAccumulator(summaries + os.listdir(summaries)[0]) 53 | accumulator.Reload() # load event files 54 | 55 | loss_ = tf.placeholder(tf.float32) 56 | ll = tf.scalar_summary('test_loglikelihood', loss_) 57 | summary_writer = tf.train.SummaryWriter(summaries, sess.graph) 58 | try: 59 | _, steps, kl_values = scalar2arrays(accumulator.Scalars('test_kl_loss')) 60 | _, steps, rec_values = scalar2arrays(accumulator.Scalars('test_rec_loss')) 61 | except: 62 | continue 63 | for step, kl, rec in zip(steps, kl_values, rec_values): 64 | summary = sess.run(ll, feed_dict={loss_: np.float32(kl + 30*rec)}) 65 | summary_writer.add_summary(summary, step) 66 | print('ok') 67 | -------------------------------------------------------------------------------- /nico_comment/vrae.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import pickle 4 | import random 5 | import time 6 | import sys 7 | import datetime 8 | import copy 9 | import numpy as np 10 | import chainer 11 | from chainer import optimizers 12 | from chainer import cuda 13 | from chainer import serializers 14 | import tensorflow as tf 15 | import multiprocessing as mp 16 | 17 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 18 | 19 | from models import SeqGAN, TextCNN, SeqEncoder 20 | # from optimizer_hook import NamedWeightDecay 21 | 22 | os.environ['PATH'] += ':/usr/local/cuda/bin' 23 | 24 | parser = argparse.ArgumentParser() 25 | parser.add_argument("--out", default='') 26 | parser.add_argument("--gen", default='') 27 | parser.add_argument("--dis", default='') 28 | parser.add_argument("--enc", default='') 29 | parser.add_argument("--init", default=0, type=int) 30 | parser.add_argument('--gpu', '-g', type=int, default=0) 31 | parser.add_argument('--parallel', '-p', default=0, type=int) 32 | 33 | # Generator Hyper-parameters 34 | parser.add_argument("--gen_emb_dim", type=int, default=256) 35 | parser.add_argument("--gen_hidden_dim", type=int, default=256) 36 | parser.add_argument("--gen_grad_clip", type=int, default=5) 37 | parser.add_argument("--gen_lr", type=float, default=1e-3) 38 | parser.add_argument("--num_lstm_layer", type=int, default=1) 39 | parser.add_argument("--no-dropout", dest='dropout', action='store_false', default=True) 40 | parser.add_argument("--kl_anneal", dest='kl_anneal', action='store_true', default=False) 41 | parser.add_argument("--kl_anneal_ratio", type=float, default=1e-3) 42 | parser.add_argument("--kl_initial", type=float, default=0) 43 | parser.add_argument("--word_drop", type=float, default=0) 44 | parser.add_argument("--concat_z", dest='latent_dim', action='store_const', const=int(128)) 45 | parser.add_argument("--concat_z_dim", dest='latent_dim', type=int) 46 | parser.add_argument('--use_tag', dest='use_tag', action='store_true', default=False) 47 | 48 | # Training Hyper-parameters 49 | parser.add_argument("--batch_size", type=int, default=100) 50 | parser.add_argument("--total_epoch", type=int, default=800) 51 | parser.add_argument("--gen_pretrain_epoch", type=int, default=100) 52 | 53 | parser.add_argument("--no_vae", dest='vae', action='store_false', default=True) 54 | 55 | args = parser.parse_args() 56 | 57 | # multiprocess worker 58 | if args.parallel > 0: 59 | pool = mp.Pool(16) 60 | else: 61 | pool = None 62 | 63 | batch_size = args.batch_size 64 | 65 | out = datetime.datetime.now().strftime('%m%d') 66 | if args.out: 67 | out += '_'+args.out 68 | out_dir = os.path.abspath(os.path.join(os.path.curdir, "runs_vrae", out)) 69 | os.makedirs(os.path.join(out_dir, 'models'), exist_ok=True) 70 | 71 | with open(os.path.join(out_dir, 'setting.txt'), 'w') as f: 72 | for k, v in args._get_kwargs(): 73 | print('{} = {}'.format(k, v)) 74 | f.write('{} = {}\n'.format(k, v)) 75 | 76 | cuda.get_device(args.gpu).use() 77 | 78 | SEED = 88 79 | random.seed(SEED) 80 | np.random.seed(SEED) 81 | 82 | # load nico-comment dataset loader 83 | with open('nico_comment_processed3.dat', 'rb') as f: 84 | train_comment_data, test_comment_data, train_tag_data, test_tag_data, vocab, tag_id = pickle.load(f) 85 | 86 | train_num = len(train_comment_data) 87 | test_num = len(test_comment_data) 88 | tag_dim = len(tag_id) if args.use_tag else 0 89 | vocab_size = 3000 90 | seq_length = 20 91 | start_token = 0 92 | 93 | # encoder 94 | encoder = SeqEncoder(vocab_size=vocab_size, emb_dim=args.gen_emb_dim, hidden_dim=args.gen_hidden_dim, 95 | latent_dim=args.latent_dim, sequence_length=seq_length, tag_num=tag_dim).to_gpu() 96 | 97 | if args.enc: 98 | serializers.load_hdf5(args.enc, encoder) 99 | 100 | # generator 101 | generator = SeqGAN(vocab_size=vocab_size, emb_dim=args.gen_emb_dim, hidden_dim=args.gen_hidden_dim, 102 | sequence_length=seq_length, start_token=start_token, lstm_layer=args.num_lstm_layer, 103 | dropout=args.dropout, encoder=encoder, latent_dim=args.latent_dim, tag_dim=tag_dim).to_gpu() 104 | if args.gen: 105 | serializers.load_hdf5(args.gen, generator) 106 | 107 | 108 | # set optimizer 109 | enc_optimizer = optimizers.Adam(alpha=args.gen_lr) 110 | enc_optimizer.setup(encoder) 111 | enc_optimizer.add_hook(chainer.optimizer.GradientClipping(args.gen_grad_clip)) 112 | 113 | gen_optimizer = optimizers.Adam(alpha=args.gen_lr) 114 | gen_optimizer.setup(generator) 115 | gen_optimizer.add_hook(chainer.optimizer.GradientClipping(args.gen_grad_clip)) 116 | 117 | # summaries 118 | sess = tf.Session() 119 | sess.run(tf.initialize_all_variables()) 120 | 121 | summary_dir = os.path.join(out_dir, "summaries") 122 | 123 | loss_ = tf.placeholder(tf.float32) 124 | train_loss_summary = tf.scalar_summary('train_loss', loss_) 125 | train_g_loss_summary = tf.scalar_summary('rec_loss', loss_) 126 | train_kl_loss_summary = tf.scalar_summary('kl_loss', loss_) 127 | test_loss_summary = tf.scalar_summary('test_loss', loss_) 128 | test_g_loss_summary = tf.scalar_summary('test_rec_loss', loss_) 129 | test_kl_loss_summary = tf.scalar_summary('test_kl_loss', loss_) 130 | 131 | summary_writer = tf.train.SummaryWriter(summary_dir, sess.graph) 132 | 133 | dis_train_count = 0 134 | gen_train_count = 0 135 | test_count = args.init 136 | 137 | with open(os.path.join(out_dir, "generated_sample_pretrain.txt"), 'w') as f: 138 | f.write('') 139 | 140 | with open(os.path.join(out_dir, "generated_sample.txt"), 'w') as f: 141 | f.write('') 142 | 143 | 144 | def progress_report(count, start_time, batchsize): 145 | duration = time.time() - start_time 146 | throughput = count * batchsize / duration 147 | sys.stderr.write( 148 | '\r{} updates ({} samples) time: {} ({:.2f} samples/sec)'.format( 149 | count, count * batchsize, str(datetime.timedelta(seconds=duration)).split('.')[0], throughput 150 | ) 151 | ) 152 | 153 | 154 | print('Start pre-training generator...') 155 | start = time.time() 156 | C = args.kl_initial 157 | for epoch in range(args.init, args.gen_pretrain_epoch): 158 | 159 | # pre-train 160 | pre_train_loss = [] 161 | sum_g_loss = [] 162 | sum_kl_loss = [] 163 | perm = np.random.permutation(train_num) 164 | if args.kl_anneal: 165 | C += args.kl_anneal_ratio 166 | 167 | for i in range(0, train_num, batch_size): 168 | x_batch = train_comment_data[perm[i:i + batch_size]] 169 | tag_batch = train_tag_data[perm[i:i + batch_size]] 170 | if args.vae: 171 | if args.use_tag: 172 | g_loss, kl_loss = generator.pretrain_step_vrae_tag(x_batch, tag_batch, args.word_drop) 173 | else: 174 | g_loss, kl_loss = generator.pretrain_step_vrae(x_batch, args.word_drop) 175 | 176 | loss = g_loss + C * kl_loss 177 | 178 | enc_optimizer.zero_grads() 179 | gen_optimizer.zero_grads() 180 | loss.backward() 181 | enc_optimizer.update() 182 | gen_optimizer.update() 183 | 184 | pre_train_loss.append(float(loss.data)) 185 | sum_g_loss.append(float(g_loss.data)) 186 | sum_kl_loss.append(float(kl_loss.data)) 187 | else: 188 | g_loss = generator.pretrain_step_autoencoder(x_batch) 189 | enc_optimizer.zero_grads() 190 | gen_optimizer.zero_grads() 191 | g_loss.backward() 192 | enc_optimizer.update() 193 | gen_optimizer.update() 194 | pre_train_loss.append(float(g_loss.data)) 195 | 196 | # progress report 197 | gen_train_count += 1 198 | progress_report(gen_train_count, start, batch_size) 199 | 200 | # test 201 | test_loss = [] 202 | sum_test_g_loss = [] 203 | sum_test_kl_loss = [] 204 | perm = np.random.permutation(test_num) 205 | 206 | for i in range(0, test_num, batch_size): 207 | x_batch = test_comment_data[perm[i:i + batch_size]] 208 | tag_batch = test_tag_data[perm[i:i + batch_size]] 209 | if args.vae: 210 | if args.use_tag: 211 | g_loss, kl_loss = generator.pretrain_step_vrae_tag(x_batch, tag_batch, args.word_drop, train=False) 212 | else: 213 | g_loss, kl_loss = generator.pretrain_step_vrae(x_batch, args.word_drop, train=False) 214 | 215 | loss = g_loss + kl_loss 216 | sum_test_g_loss.append(float(g_loss.data)) 217 | sum_test_kl_loss.append(float(kl_loss.data)) 218 | test_loss.append(float(loss.data)) 219 | 220 | else: 221 | loss = generator.pretrain_step_(x_batch) 222 | test_loss.append(float(loss.data)) 223 | 224 | test_count += 1 225 | 226 | if args.vae: 227 | print('\npre-train epoch {}'.format(epoch)) 228 | print(' train : rec_loss {} kl_loss {} loss {}'.format(np.mean(sum_g_loss), np.mean(sum_kl_loss), np.mean(pre_train_loss))) 229 | print(' test : rec_loss {} kl_loss {} loss {}'.format(np.mean(sum_test_g_loss), np.mean(sum_test_kl_loss), np.mean(test_loss))) 230 | 231 | summary = sess.run(train_loss_summary, feed_dict={loss_: np.mean(pre_train_loss)}) 232 | summary_writer.add_summary(summary, test_count) 233 | summary = sess.run(train_g_loss_summary, feed_dict={loss_: np.mean(sum_g_loss)}) 234 | summary_writer.add_summary(summary, test_count) 235 | summary = sess.run(train_kl_loss_summary, feed_dict={loss_: np.mean(sum_kl_loss)}) 236 | summary_writer.add_summary(summary, test_count) 237 | 238 | summary = sess.run(test_loss_summary, feed_dict={loss_: np.mean(test_loss)}) 239 | summary_writer.add_summary(summary, test_count) 240 | summary = sess.run(test_g_loss_summary, feed_dict={loss_: np.mean(sum_test_g_loss)}) 241 | summary_writer.add_summary(summary, test_count) 242 | summary = sess.run(test_kl_loss_summary, feed_dict={loss_: np.mean(sum_test_kl_loss)}) 243 | summary_writer.add_summary(summary, test_count) 244 | 245 | if args.use_tag: 246 | tag_batch_1 = np.repeat(range(len(tag_id)), 5) 247 | samples = generator.generate_use_tag(tag_batch_1, train=False) 248 | perm = np.random.permutation(test_num) 249 | x_batch = test_comment_data[perm[:50]] 250 | tag_batch_2 = test_tag_data[perm[:50]] 251 | autoencode_samples = generator.generate_use_tag(tag_batch_2, x_batch, train=False) 252 | with open(os.path.join(out_dir, "generated_sample_pretrain.txt"), 'a', encoding='utf-8') as f: 253 | f.write('\npre-train epoch {} train_loss {} test_loss {} \n'.format(epoch, np.mean(pre_train_loss), 254 | np.mean(test_loss))) 255 | sentences = [''.join([vocab[w] for w in x]) for i, x in enumerate(samples)] 256 | for x, y in zip(sentences, tag_batch_1): 257 | f.write(tag_id[y] + ' ' + x + '\n') 258 | 259 | sentences = [''.join([vocab[w] for w in x]) for i, x in enumerate(autoencode_samples)] 260 | for x, y in zip(sentences, tag_batch_2): 261 | f.write(tag_id[y] + ' ' + x + '\n') 262 | else: 263 | samples = generator.generate(10, train=False) 264 | with open(os.path.join(out_dir, "generated_sample_pretrain.txt"), 'a', encoding='utf-8') as f: 265 | f.write('\npre-train epoch {} train_loss {} test_loss {} \n'.format(epoch, np.mean(pre_train_loss), 266 | np.mean(test_loss))) 267 | for x in samples: 268 | f.write(''.join([vocab[w] for w in x]) + '\n') 269 | else: 270 | print('\npre-train epoch {} train_loss {} test_loss {}'.format(epoch, np.mean(pre_train_loss), np.mean(test_loss))) 271 | summary = sess.run(train_loss_summary, feed_dict={loss_: np.mean(pre_train_loss)}) 272 | summary_writer.add_summary(summary, test_count) 273 | summary = sess.run(test_loss_summary, feed_dict={loss_: np.mean(test_loss)}) 274 | summary_writer.add_summary(summary, test_count) 275 | samples = generator.generate(10, train=False) 276 | 277 | with open(os.path.join(out_dir, "generated_sample_pretrain.txt"), 'a', encoding='utf-8') as f: 278 | f.write('\npre-train epoch {} train_loss {} test_loss {} \n'.format(epoch, np.mean(pre_train_loss), 279 | np.mean(test_loss))) 280 | for x in samples: 281 | f.write(''.join([vocab[w] for w in x]) + '\n') 282 | 283 | if epoch > 0 and epoch % 5 == 0: 284 | serializers.save_hdf5(os.path.join(out_dir, "models", "gen_pretrain_{}.model".format(epoch)), generator) 285 | serializers.save_hdf5(os.path.join(out_dir, "models", "enc_pretrain_{}.model".format(epoch)), encoder) 286 | -------------------------------------------------------------------------------- /novelist_ni_narou/dataset/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fukuta0614/chainer-SeqGAN/85aa70b1b476346138e22058d527b669ee309203/novelist_ni_narou/dataset/__init__.py -------------------------------------------------------------------------------- /novelist_ni_narou/dataset/arasuji.py: -------------------------------------------------------------------------------- 1 | import re 2 | import unicodedata 3 | import numpy as np 4 | from collections import Counter 5 | import os 6 | import json 7 | import pickle 8 | 9 | 10 | class Arasuji(object): 11 | def __init__(self, raw_data, vocab_size, seq_length): 12 | 13 | self.vocab_size = vocab_size 14 | self.seq_length = seq_length 15 | 16 | self.raw_text=[] 17 | for i, j in enumerate(raw_data): 18 | x = self.clean(j['summary']) 19 | if x and len(x) < seq_length: 20 | self.raw_text.append(x) 21 | 22 | self.data_num = len(self.raw_text) 23 | print('data num',self.data_num) 24 | 25 | words = [] 26 | for line in self.raw_text: 27 | words.extend(line) 28 | words.append(' ') 29 | 30 | counter = Counter(words) 31 | self.word_freq = {word: cnt for word, cnt in counter.most_common(vocab_size-3)} 32 | self.vocab = ['_START'] + [''] + sorted(list(self.word_freq)) + [' '] 33 | self.word2idx = {word:i for i, word in enumerate(self.vocab)} 34 | 35 | print('word num',len(self.vocab)) 36 | 37 | self.data = np.ones((self.data_num, self.seq_length), np.int32) * (vocab_size-1) 38 | for i in range(self.data_num): 39 | for j in range(len(self.raw_text[i])): 40 | w = self.raw_text[i][j] 41 | if w in self.vocab: 42 | self.data[i][j] = self.word2idx[w] 43 | else: 44 | self.data[i][len(self.raw_text[i])] = 1 45 | 46 | perm = np.random.permutation(self.data_num) 47 | self.test_idx = perm[:11700] 48 | self.train_idx = perm[11700:] 49 | 50 | def clean(self, string): 51 | SpecialLetters = r"""*|¥|¥|#|#|?|×|+|†|:|;|~|¨|\xad|°|´|'̈|゙ ゚ 52 | |×|ĵ|α|β|π|σ|φ|ω|м|о|х|٩|۶|ก|ค|ง|จ|ณ|ท|\|| 53 | |น|ฟ|ม|ย|ร|ส|ห|ั|า|ิ|ี|ุ|เ|แ|ไ|่|‐|–|─|—|•|‥|′| '́|̈' 54 | |…|※|‼|⁇|⁈|⁉|⁺|℃|ⅰ|ⅱ|ⅲ|←|↑|→|↓|⇒|⇔|−|〜 |〝|\〟|〜|〟 55 | |∞|≒|≧|≪|≫|①|②|③|④|⑤|⑥|⑦|⑧|⑨|⑩|⑪|⑫|\^ 56 | |━|│|┌|┐|■|□|△|▼|▽|◆|◇|○|◎|●|◒|◯|〇|◼|〓|★|☆|♀|♂|♥|♡|♦|♪|♬|♯|⚪|⚫|✕|✖|✳|〃 57 | |\x81|\x8d|«|·|»|â|ä|è|é|ö|ø|ə|ɪ|ɲ|ʕ|̀|́|̄|̈|ά|γ|δ|ς|υ|д|з|щ|я|ᅠ|\u200f|―|‹|›|∀|√|∠|∮|∵|∽ 58 | |≋|≓|≔|≕|≖|≠|≡|≣|⊂|⊰|⊱|⊴|⋆|⋛|⋯|⌒|┏|┓|├|┤|╋|═|▄|◥|◻|◽|☓|☝|☪|☺| 59 | ♉|♠|♢|♤|♧|♭|⚠|✤|✩|✴|✽|❁|❕|❗|❪|❫|❮|❯|➖|➡|⬆|⭐|؈|'ฺ|∽|♉|,""" 60 | 61 | string = string.split('。')[0] + '。' 62 | string = ' '.join(string.split()) 63 | 64 | string = re.sub(r'{|\{|\[|『|【|《|〈|〔|〖','「',string) 65 | string = re.sub(r'{|\}|\]|』|】|》|〉|〕|〗','」', string) 66 | string = re.sub(r'[‘’“”″`\']', '"', string) 67 | string = re.sub(r"(.+?)", '', string) 68 | string = re.sub(r"\(.+?\)", '', string) 69 | string = re.sub(r"<.+?>", '', string) 70 | 71 | string = unicodedata.normalize('NFKC', string).lower() 72 | string = re.sub('https?://(\S)+', '', string) 73 | string = re.sub(SpecialLetters, '', string) 74 | string = ' '.join(string.split()) 75 | 76 | if len(set(string)) < 5: 77 | return '' 78 | return string 79 | 80 | def get_train_data(self,batch_size): 81 | idx = np.random.choice(self.train_idx, batch_size, replace=False) 82 | return self.data[idx] 83 | 84 | def get_test_data(self, batch_size): 85 | idx = np.random.choice(self.test_idx, batch_size, replace=False) 86 | return self.data[idx] 87 | 88 | 89 | if __name__ == '__main__': 90 | json_data = [] 91 | for i, f_name in enumerate(os.listdir('dataset')): 92 | try: 93 | with open('dataset/' + f_name) as f: 94 | json_data.append(json.load(f)) 95 | except: 96 | pass 97 | Loader = Arasuji(json_data, vocab_size=3000, seq_length=40) 98 | 99 | with open('arasuji.dat', 'wb') as f: 100 | pickle.dump(Loader, f) -------------------------------------------------------------------------------- /novelist_ni_narou/dataset/scrape.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import os 4 | import re 5 | import urllib.error 6 | import urllib.request 7 | import string 8 | import glob 9 | 10 | from bs4 import BeautifulSoup 11 | 12 | ROOTDIR = os.path.dirname(__file__) 13 | SAVEDIR = os.path.join(ROOTDIR, "dataset") 14 | 15 | def save(book_id: str) -> None: 16 | url = "http://ncode.syosetu.com/novelview/infotop/ncode/{}/".format(book_id) 17 | try: 18 | resource = urllib.request.urlopen(url) 19 | except urllib.error.HTTPError as e: 20 | return 21 | soup = BeautifulSoup(resource, "html.parser") 22 | info_soup = soup.find("table", {"id": "noveltable1"}) # type: BeautifulSoup 23 | if info_soup is None: # R18 24 | return 25 | info_list = list(info_soup.find_all("tr")) # type: List[BeautifulSoup] 26 | summary_line_soup = info_list[0] 27 | genre_line_soup = info_list[-1] 28 | summary = summary_line_soup.find("td").text 29 | genre = genre_line_soup.find("td").text 30 | matcher = re.match('(.*)〔(.*)〕', genre) 31 | genre_small, genre_large = matcher.groups() 32 | 33 | status_soup = soup.find("table", {"id": "noveltable2"}) # type: BeautifulSoup 34 | status_soups = status_soup.find_all("td") # type: List[BeautifulSoup] 35 | if len(status_soups) == 8: 36 | upload_date_soup, messages_soup, reviews_soup, bookmarks, total_point, points, public_satus, characters \ 37 | = status_soup.find_all("td") 38 | elif len(status_soups) == 9: 39 | upload_date_soup, update_date_soup, messages_soup, reviews_soup, bookmarks, total_point, points, public_satus, characters \ 40 | = status_soup.find_all("td") 41 | upload_date = upload_date_soup.text 42 | matcher = re.match('.*([0-9]+)件.*', messages_soup.text, flags=re.DOTALL) 43 | messages = matcher.groups()[0] if (not matcher is None) else -1 44 | matcher = re.match('.*([0-9]+)件.*', reviews_soup.text, flags=re.DOTALL) 45 | reviews = matcher.groups()[0] if (not matcher is None) else -1 46 | matcher = re.match('.*([0-9]+)件.*', bookmarks.text) 47 | bookmarks = matcher.groups()[0] if (not matcher is None) else -1 48 | matcher = re.match('.*([0-9]+)pt.*', total_point.text) 49 | total_point = matcher.groups()[0] if (not matcher is None) else -1 50 | 51 | with open(os.path.join(SAVEDIR, "{}.json".format(book_id)), "w+") as f: 52 | json.dump({ 53 | "genre_large": genre_large, 54 | "genre_small": genre_small, 55 | "summary": summary, 56 | "upload_date": upload_date, 57 | "messages": messages, 58 | "reviews": reviews, 59 | "bookmarks": bookmarks, 60 | "total_point": total_point 61 | }, f) 62 | 63 | for major_code in ["c", "d"]: 64 | for minor_code in string.ascii_lowercase: 65 | if len(glob.glob(os.path.join(SAVEDIR, "n*{}{}.json".format(major_code, minor_code)))) > 0: 66 | continue 67 | for i in range(10000): 68 | print("n{:04}{}{}".format(i, major_code, minor_code)) 69 | save("n{:04}{}{}".format(i, major_code, minor_code)) 70 | -------------------------------------------------------------------------------- /novelist_ni_narou/generate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function 3 | 4 | import argparse 5 | import chainer.serializers 6 | import os 7 | import sys 8 | import pickle 9 | import numpy as np 10 | from model import SeqGAN 11 | import time 12 | import datetime 13 | import multiprocessing as mp 14 | 15 | pool = mp.Pool() 16 | 17 | generator = SeqGAN(vocab_size=3000, emb_dim=128, hidden_dim=128, 18 | sequence_length=40, start_token=0, lstm_layer=2 19 | ).to_gpu() 20 | 21 | batch_size = 10000 22 | 23 | 24 | def progress_report(count, start_time, batch_size): 25 | duration = time.time() - start_time 26 | throughput = count * batch_size / duration 27 | sys.stderr.write( 28 | '\rtrain {} updates ({} samples) time: {} ({:.2f} samples/sec)' 29 | .format(count, count * batch_size, 30 | str(datetime.timedelta(seconds=duration)).split('.')[0], throughput)) 31 | 32 | negative = [] 33 | 34 | # pool=None 35 | st = time.time() 36 | for x in range(30000 // batch_size): 37 | negative.append(generator.generate(batch_size)) 38 | progress_report(x, st, batch_size) 39 | t = time.time() 40 | print() 41 | print(t - st) 42 | for x in range(30000 // batch_size): 43 | negative.append(generator.generate(batch_size, pool)) 44 | progress_report(x, t, batch_size) 45 | print(time.time() - t) 46 | -------------------------------------------------------------------------------- /novelist_ni_narou/run_sequence_gan.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import pickle 4 | import random 5 | import time 6 | import sys 7 | import datetime 8 | import copy 9 | import numpy as np 10 | import chainer 11 | from chainer import optimizers 12 | from chainer import cuda 13 | from chainer import serializers 14 | import tensorflow as tf 15 | import multiprocessing as mp 16 | 17 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 18 | 19 | from models import SeqGAN, TextCNN 20 | from optimizer_hook import NamedWeightDecay 21 | 22 | from dataset.arasuji import Arasuji 23 | 24 | # cuda.cudnn_enable = False 25 | 26 | os.environ['PATH'] += ':/usr/local/cuda/bin' 27 | 28 | parser = argparse.ArgumentParser() 29 | parser.add_argument("--out", default='') 30 | parser.add_argument("--gen", default='') 31 | parser.add_argument("--dis", default='') 32 | parser.add_argument('--gpu', '-g', type=int, default=0) 33 | parser.add_argument('--parallel', '-p', default=0, type=int) 34 | 35 | # Generator Hyper-parameters 36 | parser.add_argument("--gen_emb_dim", type=int, default=128) 37 | parser.add_argument("--gen_hidden_dim", type=int, default=128) 38 | parser.add_argument("--gen_grad_clip", type=int, default=5) 39 | parser.add_argument("--gen_lr", type=float, default=1e-3) 40 | parser.add_argument("--num_lstm_layer", type=int, default=2) 41 | 42 | # Discriminator Hyper-parameters 43 | parser.add_argument("--dis_embedding_dim", type=int, default=64) 44 | parser.add_argument("--dis_filter_sizes", default="1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20") 45 | parser.add_argument("--dis_num_filters", default="100, 200, 200, 200, 200, 100, 100, 100, 100, 100, 160, 160") 46 | parser.add_argument("--dis_dropout_keep_prob", type=float, default=0.75) 47 | parser.add_argument("--dis_l2_reg_lambda", type=float, default=0.2) 48 | parser.add_argument("--dis_lr", type=float, default=1e-4) 49 | 50 | # Training Hyper-parameters 51 | parser.add_argument("--batch_size", type=int, default=100) 52 | parser.add_argument("--total_epoch", type=int, default=800) 53 | parser.add_argument("--gen_pretrain_epoch", type=int, default=100) 54 | parser.add_argument("--dis_pretrain_epoch", type=int, default=50) 55 | 56 | parser.add_argument("--g_steps", type=int, default=1) 57 | parser.add_argument("--d_steps", type=int, default=5) 58 | parser.add_argument("--K", type=int, default=5) 59 | 60 | parser.add_argument("--rollout_update_ratio", type=float, default=0.8) 61 | parser.add_argument("--sample_per_iter", type=int, default=10000) 62 | 63 | args = parser.parse_args() 64 | 65 | # multiprocess worker 66 | if args.parallel > 0: 67 | pool = mp.Pool(16) 68 | else: 69 | pool = None 70 | 71 | batch_size = args.batch_size 72 | assert args.sample_per_iter % batch_size == 0 73 | 74 | if args.out: 75 | out = args.out 76 | else: 77 | out = str(int(time.time())) 78 | out_dir = os.path.abspath(os.path.join(os.path.curdir, "runs", out)) 79 | os.makedirs(os.path.join(out_dir, 'models'), exist_ok=True) 80 | 81 | with open(os.path.join(out_dir, 'setting.txt'), 'w') as f: 82 | for k, v in args._get_kwargs(): 83 | print('{} = {}'.format(k, v)) 84 | f.write('{} = {}\n'.format(k, v)) 85 | 86 | cuda.get_device(args.gpu).use() 87 | 88 | SEED = 88 89 | random.seed(SEED) 90 | np.random.seed(SEED) 91 | 92 | # load data arasuji loader 93 | with open('dataset/arasuji.dat', 'rb') as f: 94 | arasuji = pickle.load(f) 95 | 96 | train_num = len(arasuji.train_idx) 97 | test_num = len(arasuji.test_idx) 98 | vocab_size = 3000 99 | seq_length = 40 100 | start_token = 0 101 | 102 | # generator 103 | generator = SeqGAN(vocab_size=vocab_size, emb_dim=args.gen_emb_dim, hidden_dim=args.gen_hidden_dim, 104 | sequence_length=seq_length, start_token=start_token, lstm_layer=args.num_lstm_layer, 105 | dropout=True).to_gpu() 106 | if args.gen: 107 | serializers.load_hdf5(args.gen, generator) 108 | 109 | # discriminator 110 | discriminator = TextCNN(num_classes=2, vocab_size=vocab_size, embedding_size=args.dis_embedding_dim, 111 | filter_sizes=[int(n) for n in args.dis_filter_sizes.split(',')], 112 | num_filters=[int(n) for n in args.dis_num_filters.split(',')] 113 | ).to_gpu() 114 | if args.dis: 115 | serializers.load_hdf5(args.dis, discriminator) 116 | 117 | # set optimizer 118 | gen_optimizer = optimizers.Adam(alpha=args.gen_lr) 119 | gen_optimizer.setup(generator) 120 | gen_optimizer.add_hook(chainer.optimizer.GradientClipping(args.gen_grad_clip)) 121 | 122 | dis_optimizer = optimizers.Adam(alpha=args.dis_lr) 123 | dis_optimizer.setup(discriminator) 124 | dis_optimizer.add_hook(NamedWeightDecay(args.dis_l2_reg_lambda, '/out/')) 125 | 126 | # summaries 127 | sess = tf.Session() 128 | sess.run(tf.initialize_all_variables()) 129 | 130 | summary_dir = os.path.join(out_dir, "summaries") 131 | 132 | loss_ = tf.placeholder(tf.float32) 133 | train_loss_summary = tf.scalar_summary('train_loss', loss_) 134 | test_loss_summary = tf.scalar_summary('test_loss', loss_) 135 | dis_loss_summary = tf.scalar_summary('dis_loss', loss_) 136 | dis_acc_summary = tf.scalar_summary('dis_acc', loss_) 137 | 138 | summary_writer = tf.train.SummaryWriter(summary_dir, sess.graph) 139 | 140 | dis_train_count = 0 141 | gen_train_count = 0 142 | test_count = 0 143 | 144 | with open(os.path.join(out_dir, "generated_sample_pretrain.txt"), 'w') as f: 145 | f.write('') 146 | 147 | with open(os.path.join(out_dir, "generated_sample.txt"), 'w') as f: 148 | f.write('') 149 | 150 | 151 | def progress_report(count, start_time, batchsize): 152 | duration = time.time() - start_time 153 | throughput = count * batchsize / duration 154 | sys.stderr.write( 155 | '\r{} updates ({} samples) time: {} ({:.2f} samples/sec)'.format( 156 | count, count * batchsize, str(datetime.timedelta(seconds=duration)).split('.')[0], throughput 157 | ) 158 | ) 159 | 160 | 161 | if not args.gen: 162 | print('Start pre-training generator...') 163 | start = time.time() 164 | for epoch in range(args.gen_pretrain_epoch): 165 | 166 | # pre-train 167 | pre_train_loss = [] 168 | for _ in range(train_num // batch_size): 169 | batch = arasuji.get_train_data(batch_size) 170 | g_loss = generator.pretrain_step(batch) 171 | gen_optimizer.zero_grads() 172 | g_loss.backward() 173 | gen_optimizer.update() 174 | pre_train_loss.append(float(g_loss.data)) 175 | 176 | # progress report 177 | gen_train_count += 1 178 | progress_report(gen_train_count, start, batch_size) 179 | 180 | # test 181 | test_loss = [] 182 | for _ in range(test_num // batch_size): 183 | batch = arasuji.get_test_data(batch_size) 184 | g_loss = generator.pretrain_step(batch) 185 | test_loss.append(float(g_loss.data)) 186 | test_count += 1 187 | 188 | print('\npre-train epoch {} train_loss {} test_loss {}'.format(epoch, np.mean(pre_train_loss), 189 | np.mean(test_loss))) 190 | summary = sess.run(train_loss_summary, feed_dict={loss_: np.mean(pre_train_loss)}) 191 | summary_writer.add_summary(summary, test_count) 192 | summary = sess.run(test_loss_summary, feed_dict={loss_: np.mean(test_loss)}) 193 | summary_writer.add_summary(summary, test_count) 194 | samples = generator.generate(10, train=False) 195 | with open(os.path.join(out_dir, "generated_sample_pretrain.txt"), 'a', encoding='utf-8') as f: 196 | f.write('\npre-train epoch {} train_loss {} test_loss {} \n'.format(epoch, np.mean(pre_train_loss), 197 | np.mean(test_loss))) 198 | for x in samples: 199 | f.write(''.join([arasuji.vocab[w] for w in x]) + '\n') 200 | 201 | serializers.save_hdf5(os.path.join(out_dir, "models", "gen_pretrain.model"), generator) 202 | 203 | else: 204 | # test 205 | test_loss = [] 206 | for _ in range(test_num // batch_size): 207 | batch = arasuji.get_test_data(batch_size) 208 | g_loss = generator.pretrain_step(batch) 209 | test_loss.append(float(g_loss.data)) 210 | print('\npre-trained test_loss {}'.format(np.mean(test_loss))) 211 | test_count = args.gen_pretrain_epoch 212 | summary = sess.run(test_loss_summary, feed_dict={loss_: np.mean(test_loss)}) 213 | summary_writer.add_summary(summary, test_count) 214 | 215 | # discriminator pre-train 216 | if not args.dis: 217 | train_count = 0 218 | start = time.time() 219 | print('Start pre-training discriminator...') 220 | 221 | for epoch in range(args.dis_pretrain_epoch): 222 | 223 | # negative = np.vstack([generator.generate(batch_size, pool=pool) for x in range(args.sample_per_iter // batch_size)]) 224 | negative = generator.generate(args.sample_per_iter) 225 | 226 | for k in range(args.K): 227 | positive = arasuji.get_train_data(args.sample_per_iter) 228 | 229 | x = np.vstack([positive, negative]) 230 | y = np.array([1] * args.sample_per_iter + [0] * args.sample_per_iter) 231 | sum_train_loss = [] 232 | sum_train_accuracy = [] 233 | perm = np.random.permutation(len(y)) 234 | 235 | for i in range(0, len(y), batch_size): 236 | x_batch = x[perm[i:i + batch_size]] 237 | y_batch = y[perm[i:i + batch_size]] 238 | loss, acc = discriminator(x_batch, y_batch, args.dis_dropout_keep_prob) 239 | dis_optimizer.zero_grads() 240 | loss.backward() 241 | dis_optimizer.update() 242 | sum_train_loss.append(float(loss.data)) 243 | sum_train_accuracy.append(float(acc.data)) 244 | 245 | train_count += 1 246 | progress_report(train_count, start, batch_size) 247 | 248 | print('\ndis-train epoch ', epoch, 'train_loss ', np.mean(sum_train_loss), 'train_acc ', 249 | np.mean(sum_train_accuracy)) 250 | dis_train_count += 1 251 | summary = sess.run(dis_loss_summary, feed_dict={loss_: np.mean(sum_train_loss)}) 252 | summary_writer.add_summary(summary, dis_train_count) 253 | summary = sess.run(dis_acc_summary, feed_dict={loss_: np.mean(sum_train_accuracy)}) 254 | summary_writer.add_summary(summary, dis_train_count) 255 | 256 | serializers.save_hdf5(os.path.join(out_dir, "models", "dis_pretrain.model"), discriminator) 257 | 258 | gen_optimizer = optimizers.Adam(alpha=args.gen_lr) 259 | gen_optimizer.setup(generator) 260 | gen_optimizer.add_hook(chainer.optimizer.GradientClipping(args.gen_grad_clip)) 261 | 262 | # roll out generator 263 | rollout_generator = copy.deepcopy(generator).to_cpu() 264 | rollout_params = np.asanyarray(tuple(param.data for param in rollout_generator.params())) 265 | 266 | print('#########################################################################\n') 267 | print('Start Reinforcement Training ...') 268 | 269 | start = time.time() 270 | for epoch in range(1, args.total_epoch): 271 | 272 | print('total epoch ', epoch) 273 | tmp = time.time() 274 | # g-step 275 | mean_time = 0 276 | for step in range(args.g_steps): 277 | samples = generator.generate(batch_size, train=True) 278 | rewards = rollout_generator.get_rewards(samples, discriminator, pool=pool, gpu=args.gpu) 279 | loss = generator.reinforcement_step(samples, rewards, g_steps=args.g_steps) 280 | gen_optimizer.zero_grads() 281 | loss.backward() 282 | gen_optimizer.update() 283 | 284 | duration = time.time() - start 285 | step_time = time.time() - tmp 286 | mean_time += step_time 287 | tmp = time.time() 288 | sys.stderr.write('\rreinforce step {}/{} time: {} ({:.2f} sec/step)'.format( 289 | step + 1, args.g_steps, str(datetime.timedelta(seconds=duration)).split('.')[0], step_time)) 290 | else: 291 | print('\rreinforce step {}/{} time: {} ({:.2f} sec/step)'.format( 292 | step + 1, args.g_steps, str(datetime.timedelta(seconds=duration)).split('.')[0], mean_time/args.g_steps)) 293 | 294 | # update rollout generator 295 | for i, param in enumerate(generator.params()): 296 | rollout_params[i] += args.rollout_update_ratio * (cuda.to_cpu(param.data) - rollout_params[i]) 297 | 298 | # d-step 299 | mean_time = 0 300 | for step in range(args.d_steps): 301 | 302 | # negative = np.vstack([generator.generate(batch_size, pool=pool) for x in range(args.sample_per_iter // batch_size)]) 303 | negative = generator.generate(args.sample_per_iter) 304 | 305 | for i in range(args.K): 306 | positive = arasuji.get_train_data(args.sample_per_iter) 307 | 308 | x = np.vstack([positive, negative]) 309 | y = np.array([1] * args.sample_per_iter + [0] * args.sample_per_iter) 310 | sum_train_loss = [] 311 | sum_train_accuracy = [] 312 | perm = np.random.permutation(len(y)) 313 | 314 | for k in range(0, len(y), batch_size): 315 | x_batch = x[perm[i:i + batch_size]] 316 | y_batch = y[perm[i:i + batch_size]] 317 | loss, acc = discriminator(x_batch, y_batch, args.dis_dropout_keep_prob) 318 | dis_optimizer.zero_grads() 319 | loss.backward() 320 | dis_optimizer.update() 321 | sum_train_loss.append(float(loss.data)) 322 | sum_train_accuracy.append(float(acc.data)) 323 | 324 | dis_train_count += 1 325 | summary = sess.run(dis_loss_summary, feed_dict={loss_: np.mean(sum_train_loss)}) 326 | summary_writer.add_summary(summary, dis_train_count) 327 | summary = sess.run(dis_acc_summary, feed_dict={loss_: np.mean(sum_train_accuracy)}) 328 | summary_writer.add_summary(summary, dis_train_count) 329 | 330 | duration = time.time() - start 331 | step_time = time.time() - tmp 332 | mean_time += step_time 333 | tmp = time.time() 334 | sys.stderr.write('\rdis-train step {}/{} time: {} ({:.2f} sec/step)'.format( 335 | step + 1, args.d_steps, str(datetime.timedelta(seconds=duration)).split('.')[0], step_time)) 336 | else: 337 | print('\rdis-train step {}/{} time: {} ({:.2f} sec/step)'.format( 338 | step + 1, args.d_steps, str(datetime.timedelta(seconds=duration)).split('.')[0], mean_time / args.d_steps)) 339 | 340 | test_loss = [] 341 | for _ in range(test_num // batch_size): 342 | batch = arasuji.get_test_data(batch_size) 343 | g_loss = generator.pretrain_step(batch) 344 | test_loss.append(float(g_loss.data)) 345 | 346 | print('test_loss {}'.format(np.mean(test_loss))) 347 | test_count += 1 348 | summary = sess.run(test_loss_summary, feed_dict={loss_: np.mean(test_loss)}) 349 | summary_writer.add_summary(summary, test_count) 350 | samples = generator.generate(10, train=False) 351 | with open(os.path.join(out_dir, "generated_sample.txt"), 'a', encoding='utf-8') as f: 352 | f.write('\ntotal epoch {} test_loss {} \n'.format(epoch, np.mean(test_loss))) 353 | for x in samples: 354 | f.write(''.join([arasuji.vocab[w] for w in x]) + '\n') 355 | 356 | if epoch % 10 == 0: 357 | serializers.save_hdf5(os.path.join(out_dir, "models/gen_{:03d}.model".format(epoch)), generator) 358 | serializers.save_hdf5(os.path.join(out_dir, "models/dis_{:03d}.model".format(epoch)), discriminator) 359 | -------------------------------------------------------------------------------- /novelist_ni_narou/seq2seq.py: -------------------------------------------------------------------------------- 1 | 2 | import chainer 3 | import chainer.functions as F 4 | import chainer.links as L 5 | from chainer import cuda 6 | import numpy as np 7 | from chainer.cuda import cupy as xp 8 | # import temptemp 9 | 10 | 11 | class Encoder(chainer.Chain): 12 | def __init__(self, vocab_size, emb_dim, hidden_dim, 13 | sequence_length): 14 | self.vocab_size = vocab_size 15 | self.emb_dim = emb_dim 16 | self.hidden_dim = hidden_dim 17 | self.sequence_length = sequence_length 18 | 19 | super(Encoder, self).__init__( 20 | embed=L.EmbedID(self.vocab_size, self.emb_dim), 21 | lstm1=L.LSTM(self.emb_dim, self.hidden_dim), 22 | lstm2=L.LSTM(self.hidden_dim, self.hidden_dim), 23 | out=L.Linear(self.hidden_dim, self.vocab_size), 24 | ) 25 | 26 | def reset_state(self): 27 | if hasattr(self, "lstm1"): 28 | self.lstm1.reset_state() 29 | if hasattr(self, "lstm2"): 30 | self.lstm2.reset_state() 31 | 32 | def encode(self, x_input, train=True): 33 | self.reset_state() 34 | batch_size = len(x_input) 35 | for i in range(self.sequence_length): 36 | x = chainer.Variable(xp.asanyarray(x_input[:, i], 'int32')) 37 | h0 = self.embed(x) 38 | h1 = self.lstm1(F.dropout(h0, train=train)) 39 | h2 = self.lstm2(F.dropout(h1, train=train)) 40 | return h2 41 | 42 | 43 | class SeqGAN(chainer.Chain): 44 | def __init__(self, vocab_size, emb_dim, hidden_dim, 45 | sequence_length, start_token, 46 | reward_gamma=0.95, lstm_layer=1): 47 | 48 | self.vocab_size = vocab_size 49 | self.emb_dim = emb_dim 50 | self.hidden_dim = hidden_dim 51 | self.sequence_length = sequence_length 52 | self.start_token = start_token 53 | self.reward_gamma = reward_gamma 54 | self.g_params = [] 55 | self.d_params = [] 56 | self.temperature = 1.0 57 | 58 | self.encoder = Encoder(vocab_size, emb_dim, hidden_dim, sequence_length) 59 | 60 | if lstm_layer == 2: 61 | super(SeqGAN, self).__init__( 62 | embed=L.EmbedID(self.vocab_size, self.emb_dim), 63 | lstm1=L.LSTM(self.emb_dim, self.hidden_dim), 64 | lstm2=L.LSTM(self.hidden_dim, self.hidden_dim), 65 | # lstm3=L.LSTM(self.hidden_dim, self.hidden_dim), 66 | out=L.Linear(self.hidden_dim, self.vocab_size), 67 | ) 68 | else: 69 | super(SeqGAN, self).__init__( 70 | embed=L.EmbedID(self.vocab_size, self.emb_dim), 71 | lstm1=L.LSTM(self.emb_dim, self.hidden_dim), 72 | out=L.Linear(self.hidden_dim, self.vocab_size), 73 | ) 74 | 75 | def reset_state(self): 76 | if hasattr(self, "lstm1"): 77 | self.lstm1.reset_state() 78 | if hasattr(self, "lstm2"): 79 | self.lstm2.reset_state() 80 | # if hasattr(self, "lstm3"): 81 | # self.lstm3.reset_state() 82 | 83 | def decode_one_step(self, x, train=True): 84 | h0 = self.embed(x) 85 | h1 = self.lstm1(F.dropout(h0, train=train)) 86 | h2 = self.lstm2(F.dropout(h1, train=train)) 87 | y = self.out(h2) 88 | return y 89 | 90 | def generate(self, batch_size, train=False): 91 | """ 92 | :return: (batch_size, self.seq_length) 93 | """ 94 | 95 | self.reset_state() 96 | x = chainer.Variable(xp.asanyarray([self.start_token] * batch_size, 'int32'), volatile=True) 97 | gen_x = np.zeros((batch_size, self.sequence_length), 'int32') 98 | # gen_p = np.zeros((batch_size, self.sequence_length)) 99 | 100 | for i in range(self.sequence_length): 101 | scores = self.decode_one_step(x, train) 102 | pred = F.softmax(scores) 103 | pred = cuda.to_cpu(pred.data) - np.finfo(np.float32).epsneg 104 | 105 | generated = [] 106 | for j in range(batch_size): 107 | histogram = np.random.multinomial(1, pred[j]) 108 | generated.append(int(np.nonzero(histogram)[0])) 109 | 110 | gen_x[:, i] = generated 111 | x = chainer.Variable(xp.asanyarray(generated, 'int32'), volatile=True) 112 | 113 | return gen_x 114 | 115 | def pretrain_step_autoencoder(self, x_input, context): 116 | """ 117 | Maximum likelihood Estimation 118 | 119 | :param x_input: 120 | :return: loss 121 | """ 122 | self.reset_state() 123 | batch_size = len(x_input) 124 | accum_loss = 0 125 | self.lstm1.h = context[0] 126 | self.lstm2.h = context[1] 127 | 128 | for i in range(self.sequence_length): 129 | if i == 0: 130 | x = chainer.Variable(xp.asanyarray([self.start_token] * batch_size, 'int32')) 131 | else: 132 | x = chainer.Variable(xp.asanyarray(x_input[:, i - 1], 'int32')) 133 | 134 | scores = self.decode_one_step(x) 135 | loss = F.softmax_cross_entropy(scores, chainer.Variable(xp.asanyarray(x_input[:, i], 'int32'))) 136 | accum_loss += loss 137 | 138 | return accum_loss / self.sequence_length 139 | 140 | def pretrain_step(self, x_input): 141 | """ 142 | Maximum likelihood Estimation 143 | 144 | :param x_input: 145 | :return: loss 146 | """ 147 | self.reset_state() 148 | batch_size = len(x_input) 149 | accum_loss = 0 150 | for i in range(self.sequence_length): 151 | if i == 0: 152 | x = chainer.Variable(xp.asanyarray([self.start_token] * batch_size, 'int32')) 153 | else: 154 | x = chainer.Variable(xp.asanyarray(x_input[:, i - 1], 'int32')) 155 | 156 | scores = self.decode_one_step(x) 157 | loss = F.softmax_cross_entropy(scores, chainer.Variable(xp.asanyarray(x_input[:, i], 'int32'))) 158 | accum_loss += loss 159 | 160 | return accum_loss / self.sequence_length 161 | 162 | def reinforcement_step(self, x_input, rewards): 163 | """ 164 | :param x_input: (batch_size, seq_length) 165 | :param rewards: (batch_size, seq_length) 166 | :return: 167 | """ 168 | self.reset_state() 169 | batch_size = len(x_input) 170 | accum_loss = 0 171 | for j in range(self.sequence_length): 172 | if j == 0: 173 | x = chainer.Variable(xp.asanyarray([self.start_token] * batch_size, 'int32')) 174 | else: 175 | x = chainer.Variable(xp.asanyarray(x_input[:, j - 1], 'int32')) 176 | 177 | scores = self.decode_one_step(x) 178 | log_prob = F.log_softmax(scores) # (batch_size, vocab_size) 179 | loss = 0 180 | for i in range(batch_size): 181 | loss += log_prob[i, x_input[i, j]] * rewards[i, j] 182 | accum_loss += loss 183 | 184 | return -accum_loss 185 | 186 | def get_rewards(self, samples, dis, rollout_num=16): 187 | """ 188 | get reward from generated sample FOR ROLLOUT 189 | 190 | :param samples: generated_sample (batch, seq_length) 191 | :param dis: discriminator 192 | :param rollout_num: num of roll out 193 | 194 | :return: (batch, seq_length) rewards[i,j] means rewards of a_{j-1} of batch i 195 | """ 196 | 197 | batch_size = len(samples) 198 | reward_mat = np.zeros((batch_size, self.sequence_length), 'float32') 199 | for given in range(1, 20): 200 | rewards = self.roll_out(samples, given, dis, rollout_num) 201 | reward_mat[:, given - 1] = rewards 202 | 203 | reward_mat[:, 19] = dis.get_reward(samples) 204 | return reward_mat 205 | 206 | def roll_out(self, samples, given, dis, rollout_num): 207 | """ 208 | compute expected rewards 209 | 210 | :param samples: generated_sample 211 | :param given: use x_0 ~ x_given as generated (state) 212 | :param dis: discriminator 213 | :param rollout_num: num of roll out 214 | 215 | :return: rewards (batch_size) 216 | """ 217 | 218 | batch_size = len(samples) 219 | self.reset_state() 220 | gen_x = np.zeros((batch_size, self.sequence_length), 'int32') 221 | 222 | x = chainer.Variable(xp.asanyarray([self.start_token] * batch_size, 'int32'), volatile=True) 223 | self.decode_one_step(x, False) 224 | for i in range(given): 225 | gen_x[:, i] = samples[:, i] 226 | x = chainer.Variable(xp.asanyarray(samples[:, i], 'int32'), volatile=True) 227 | scores = self.decode_one_step(x, False) 228 | 229 | scores_ = scores 230 | c1, h1 = self.lstm1.c, self.lstm1.h 231 | c2, h2 = self.lstm2.c, self.lstm2.h 232 | 233 | rewards = [] 234 | for _ in range(rollout_num): 235 | self.lstm1.set_state(chainer.Variable(c1.data.copy(), volatile=True), chainer.Variable(h1.data.copy(), volatile=True)) 236 | self.lstm2.set_state(chainer.Variable(c2.data.copy(), volatile=True), chainer.Variable(h2.data.copy(), volatile=True)) 237 | 238 | scores = chainer.Variable(scores_.data.copy(), volatile=True) 239 | for i in range(given, self.sequence_length): 240 | 241 | pred = F.softmax(scores) 242 | pred = cuda.to_cpu(pred.data) - np.finfo(np.float32).epsneg 243 | 244 | generated = [] 245 | for j in range(batch_size): 246 | histogram = np.random.multinomial(1, pred[j]) 247 | generated.append(int(np.nonzero(histogram)[0])) 248 | 249 | gen_x[:, i] = generated 250 | x = chainer.Variable(xp.asanyarray(generated, 'int32'), volatile=True) 251 | scores = self.decode_one_step(x) 252 | 253 | rewards.append(dis.get_reward(gen_x)) 254 | 255 | return np.mean(rewards, axis=0) 256 | 257 | def target_loss(self, target_lstm, generated_num, batch_size, writer, summary_op, test_count, sess): 258 | 259 | # Generated Samples 260 | generated_samples = [] 261 | supervised_g_losses = [] 262 | 263 | for _ in range(int(generated_num / batch_size)): 264 | gen = list(self.generate(batch_size)) 265 | generated_samples.extend(gen) 266 | g_loss, summary = sess.run([target_lstm.pretrain_loss, summary_op], {target_lstm.x: gen}) 267 | supervised_g_losses.append(g_loss) 268 | writer.add_summary(summary, test_count) 269 | test_count += 1 270 | 271 | return np.mean(supervised_g_losses), test_count 272 | 273 | 274 | 275 | 276 | -------------------------------------------------------------------------------- /optimizer_hook/__init__.py: -------------------------------------------------------------------------------- 1 | from . import named_weight_decay 2 | 3 | NamedWeightDecay = named_weight_decay.NamedWeightDecay 4 | 5 | -------------------------------------------------------------------------------- /optimizer_hook/named_weight_decay.py: -------------------------------------------------------------------------------- 1 | from chainer import cuda 2 | 3 | 4 | class NamedWeightDecay(object): 5 | 6 | """Optimizer hook function for specific weight decay regularization. 7 | 8 | This hook function adds a scaled parameter to the corresponding gradient. 9 | It can be used as a regularization. 10 | 11 | Args: 12 | rate (float): Coefficient for the weight decay. 13 | name (str) : Name of layer to be regularized 14 | 15 | Attributes: 16 | rate (float): Coefficient for the weight decay. 17 | 18 | """ 19 | name = 'WeightDecay' 20 | 21 | def __init__(self, rate, name): 22 | self.rate = rate 23 | self.name = name 24 | 25 | def __call__(self, opt): 26 | if cuda.available: 27 | kernel = cuda.elementwise( 28 | 'T p, T decay', 'T g', 'g += decay * p', 'weight_decay') 29 | 30 | rate = self.rate 31 | for name, param in opt.target.namedparams(): 32 | if self.name in name: 33 | p, g = param.data, param.grad 34 | with cuda.get_device(p) as dev: 35 | if int(dev) == -1: 36 | g += rate * p 37 | else: 38 | kernel(p, rate, g) 39 | -------------------------------------------------------------------------------- /oracle_test/README.md: -------------------------------------------------------------------------------- 1 | # oracle test 2 | 3 | ### Requirement 4 | - Chainer 5 | - TensorFlow 0.11 (for target lstm) -------------------------------------------------------------------------------- /oracle_test/run_sequence_gan.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | 4 | import sys 5 | import pickle 6 | import random 7 | import time 8 | import copy 9 | import multiprocessing as mp 10 | import numpy as np 11 | import chainer 12 | from chainer import optimizers 13 | from chainer import cuda 14 | from chainer import serializers 15 | import tensorflow as tf 16 | 17 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 18 | 19 | from utils_oracle import TARGET_LSTM, Gen_Data_loader, Dis_dataloader, Likelihood_data_loader 20 | from models import SeqGAN, TextCNN 21 | from optimizer_hook import NamedWeightDecay 22 | 23 | 24 | np.set_printoptions(formatter={'float': '{: 0.3f}'.format}) 25 | 26 | os.environ['PATH'] += ':/usr/local/cuda/bin' 27 | 28 | parser = argparse.ArgumentParser() 29 | parser.add_argument("--out", default='') 30 | parser.add_argument("--gen", default='') 31 | parser.add_argument("--genopt", default='') 32 | parser.add_argument("--dis", default='') 33 | parser.add_argument('--gpu', '-g', default=0, type=int) 34 | parser.add_argument('--parallel', '-p', default=0, type=int) 35 | args = parser.parse_args() 36 | 37 | if args.parallel > 0: 38 | pool = mp.Pool(16) 39 | else: 40 | pool = None 41 | 42 | if args.out: 43 | out = args.out 44 | else: 45 | out = str(int(time.time())) 46 | out_dir = os.path.abspath(os.path.join(os.path.curdir, "runs", out)) 47 | os.makedirs(os.path.join(out_dir, 'models'), exist_ok=True) 48 | 49 | cuda.get_device(args.gpu).use() 50 | 51 | 52 | ######################################################################################### 53 | # Generator Hyper-parameters 54 | ######################################################################################### 55 | gen_emb_dim = 32 56 | gen_hidden_dim = 32 57 | gen_grad_clip = 5 58 | gen_batch_size = 64 59 | ######################################################################################### 60 | # Discriminator Hyper-parameters 61 | ######################################################################################### 62 | dis_embedding_dim = 64 63 | dis_filter_sizes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20] 64 | dis_num_filters = [100, 200, 200, 200, 200, 100, 100, 100, 100, 100, 160, 160] 65 | dis_dropout_keep_prob = 0.75 66 | dis_l2_reg_lambda = 0.2 67 | dis_batch_size = 64 68 | ######################################################################################### 69 | # Training Hyper-parameters 70 | ######################################################################################### 71 | total_epoch = 800 72 | gen_pretrain_epoch = 200 73 | dis_pretrain_epoch = 50 74 | 75 | rollout_update_ratio = 0.8 76 | 77 | g_steps = 1 78 | d_steps = 5 79 | K = 3 80 | 81 | SEED = 88 82 | ######################################################################################### 83 | 84 | 85 | def generate_samples_pos(session, trainable_model, batch_size, gen_num, output_file): 86 | # Generated Samples 87 | generated_samples = [] 88 | for _ in range(int(gen_num / batch_size)): 89 | generated_samples.extend(trainable_model.generate(session)) 90 | 91 | with open(output_file, 'w') as fout: 92 | for poem in generated_samples: 93 | buf = ' '.join([str(x) for x in poem]) + '\n' 94 | fout.write(buf) 95 | 96 | 97 | def generate_samples_neg(trainable_model, batch_size, gen_num, output_file): 98 | # Generated Samples 99 | # generated_samples = [] 100 | # for _ in range(int(gen_num / batch_size)): 101 | # generated_samples.extend(trainable_model.generate(batch_size, train=False)) 102 | generated_samples = trainable_model.generate(gen_num, train=False) 103 | 104 | with open(output_file, 'w') as fout: 105 | for poem in generated_samples: 106 | buf = ' '.join([str(x) for x in poem]) + '\n' 107 | fout.write(buf) 108 | 109 | 110 | def target_loss(sess, target_lstm, data_loader): 111 | supervised_g_losses = [] 112 | data_loader.reset_pointer() 113 | 114 | for it in range(data_loader.num_batch): 115 | batch = data_loader.next_batch() 116 | g_loss = sess.run(target_lstm.pretrain_loss, {target_lstm.x: batch}) 117 | supervised_g_losses.append(g_loss) 118 | 119 | return np.mean(supervised_g_losses) 120 | 121 | 122 | def significance_test(session, target_lstm, data_loader, output_file): 123 | loss = [] 124 | data_loader.reset_pointer() 125 | 126 | for it in range(data_loader.num_batch): 127 | batch = data_loader.next_batch() 128 | g_loss = session.run(target_lstm.out_loss, {target_lstm.x: batch}) 129 | loss.extend(list(g_loss)) 130 | with open(output_file, 'w')as fout: 131 | for item in loss: 132 | buffer = str(item) + '\n' 133 | fout.write(buffer) 134 | 135 | random.seed(SEED) 136 | np.random.seed(SEED) 137 | 138 | positive_file = 'save/real_data.txt' 139 | negative_file = os.path.join(out_dir, 'generator_sample.txt') 140 | eval_file = os.path.join(out_dir, 'eval.txt') 141 | generated_num = 10000 142 | 143 | gen_data_loader = Gen_Data_loader(gen_batch_size) 144 | likelihood_data_loader = Likelihood_data_loader(gen_batch_size) 145 | dis_data_loader = Dis_dataloader() 146 | vocab_size = 5000 147 | seq_length = 20 148 | best_score = 1000 149 | target_params = pickle.load(open('save/target_params.pkl', 'rb'), encoding='bytes') 150 | target_lstm = TARGET_LSTM(vocab_size, 64, 32, 32, 20, 0, target_params) 151 | start_token = 0 152 | 153 | # generator 154 | generator = SeqGAN(seq_length, vocab_size, gen_emb_dim, gen_hidden_dim, start_token, oracle=True).to_gpu() 155 | if args.gen: 156 | print(args.gen) 157 | serializers.load_hdf5(args.gen, generator) 158 | 159 | # discriminator 160 | discriminator = TextCNN(num_classes=2, vocab_size=vocab_size, 161 | embedding_size=dis_embedding_dim, 162 | filter_sizes=dis_filter_sizes, num_filters=dis_num_filters).to_gpu() 163 | if args.dis: 164 | serializers.load_hdf5(args.dis, discriminator) 165 | 166 | sess = tf.Session() 167 | sess.run(tf.initialize_all_variables()) 168 | gen_data_loader.create_batches(positive_file) 169 | generate_samples_pos(sess, target_lstm, 64, 10000, positive_file) 170 | 171 | # summaries 172 | summary_dir = os.path.join(out_dir, "summaries") 173 | 174 | loss_ = tf.placeholder(tf.float32) 175 | target_loss_summary = tf.scalar_summary('target_loss', loss_) 176 | dis_loss_summary = tf.scalar_summary('dis_loss', loss_) 177 | dis_acc_summary = tf.scalar_summary('dis_acc', loss_) 178 | 179 | summary_writer = tf.train.SummaryWriter(summary_dir, sess.graph) 180 | 181 | dis_train_count = 0 182 | test_count = 0 183 | 184 | gen_optimizer = optimizers.Adam(alpha=1e-3) 185 | gen_optimizer.setup(generator) 186 | gen_optimizer.add_hook(chainer.optimizer.GradientClipping(gen_grad_clip)) 187 | 188 | dis_optimizer = optimizers.Adam(alpha=1e-4) 189 | dis_optimizer.setup(discriminator) 190 | dis_optimizer.add_hook(NamedWeightDecay(dis_l2_reg_lambda, '/out/')) 191 | 192 | if not args.gen: 193 | 194 | print('Start pre-training generator...') 195 | 196 | for epoch in range(gen_pretrain_epoch): 197 | 198 | pre_train_loss = [] 199 | for _ in range(gen_data_loader.num_batch): 200 | batch = gen_data_loader.next_batch() 201 | g_loss = generator.pretrain_step(batch) 202 | gen_optimizer.zero_grads() 203 | g_loss.backward() 204 | gen_optimizer.update() 205 | pre_train_loss.append(g_loss.data) 206 | 207 | # print('pre-train epoch ', epoch, 'train_loss ', np.mean(pre_train_loss)) 208 | generate_samples_neg(generator, gen_batch_size, 1000, eval_file) 209 | likelihood_data_loader.create_batches(eval_file) 210 | test_loss = target_loss(sess, target_lstm, likelihood_data_loader) 211 | print('pre-train epoch: {} test loss: {} lr: {}'.format(epoch, test_loss, gen_optimizer.lr)) 212 | test_count += 1 213 | summary = sess.run(target_loss_summary, feed_dict={loss_: test_loss}) 214 | summary_writer.add_summary(summary, test_count) 215 | 216 | serializers.save_hdf5(os.path.join(out_dir, "models", "gen_pretrain.model"), generator) 217 | serializers.save_hdf5(os.path.join(out_dir, "models", "gen_pretrain.opt"), gen_optimizer) 218 | 219 | else: 220 | test_count = gen_pretrain_epoch 221 | test_loss = generator.target_loss(target_lstm, 1000, gen_batch_size, sess) 222 | summary = sess.run(target_loss_summary, feed_dict={loss_: test_loss}) 223 | summary_writer.add_summary(summary, test_count) 224 | print('After pre-training:' + ' ' + str(test_loss)) 225 | 226 | gen_optimizer = optimizers.Adam(alpha=1e-3) 227 | gen_optimizer.setup(generator) 228 | gen_optimizer.add_hook(chainer.optimizer.GradientClipping(gen_grad_clip)) 229 | 230 | if not args.dis: 231 | 232 | for epoch in range(dis_pretrain_epoch): 233 | 234 | generate_samples_neg(generator, gen_batch_size, generated_num, negative_file) 235 | 236 | # train discriminator 237 | dis_x_train, dis_y_train = dis_data_loader.load_train_data(positive_file, negative_file) 238 | dis_batches = dis_data_loader.batch_iter(zip(dis_x_train, dis_y_train), dis_batch_size, K) 239 | 240 | sum_train_loss = [] 241 | sum_train_accuracy = [] 242 | 243 | for batch in dis_batches: 244 | try: 245 | x_batch, y_batch = zip(*batch) 246 | loss, acc = discriminator(x_batch, y_batch, dis_dropout_keep_prob) 247 | dis_optimizer.zero_grads() 248 | loss.backward() 249 | dis_optimizer.update() 250 | sum_train_loss.append(float(loss.data)) 251 | sum_train_accuracy.append(float(acc.data)) 252 | except ValueError: 253 | pass 254 | 255 | print('dis-train epoch ', epoch, 'train_loss ', np.mean(sum_train_loss), 'train_acc ', np.mean(sum_train_accuracy)) 256 | dis_train_count += 1 257 | summary = sess.run(dis_loss_summary, feed_dict={loss_: np.mean(sum_train_loss)}) 258 | summary_writer.add_summary(summary, dis_train_count) 259 | summary = sess.run(dis_acc_summary, feed_dict={loss_: np.mean(sum_train_accuracy)}) 260 | summary_writer.add_summary(summary, dis_train_count) 261 | serializers.save_hdf5(os.path.join(out_dir, "models", "dis_pretrain.model"), discriminator) 262 | serializers.save_hdf5(os.path.join(out_dir, "models", "dis_pretrain.opt"), dis_optimizer) 263 | 264 | # roll out generator 265 | rollout_generator = copy.deepcopy(generator) 266 | if pool: 267 | rollout_generator.to_cpu() 268 | rollout_params = np.asanyarray(tuple(param.data for param in rollout_generator.params())) 269 | 270 | print('#########################################################################') 271 | print('Start Reinforcement Training ...') 272 | 273 | for epoch in range(1, total_epoch): 274 | 275 | print('total batch: ', epoch) 276 | 277 | for step in range(g_steps): 278 | samples = generator.generate(gen_batch_size, train=True, random_input=True) 279 | rewards = rollout_generator.get_rewards(samples, discriminator, rollout_num=16, pool=pool, gpu=args.gpu) 280 | print(rewards[:30]) 281 | loss = generator.reinforcement_step(samples, rewards, g_steps=g_steps, random_input=True) 282 | gen_optimizer.zero_grads() 283 | loss.backward() 284 | gen_optimizer.update() 285 | print(' Reinforce step {}/{}'.format(step+1, g_steps)) 286 | 287 | for i, param in enumerate(generator.params()): 288 | if pool: 289 | rollout_params[i] += rollout_update_ratio * (cuda.to_cpu(param.data) - rollout_params[i]) 290 | else: 291 | rollout_params[i] += rollout_update_ratio * (param.data - rollout_params[i]) 292 | 293 | for step in range(d_steps): 294 | 295 | # generate for discriminator 296 | generate_samples_neg(generator, gen_batch_size, generated_num, negative_file) 297 | 298 | # train discriminator 299 | dis_x_train, dis_y_train = dis_data_loader.load_train_data(positive_file, negative_file) 300 | dis_batches = dis_data_loader.batch_iter(zip(dis_x_train, dis_y_train), dis_batch_size, K) 301 | sum_train_loss = [] 302 | sum_train_accuracy = [] 303 | flag = True 304 | for batch in dis_batches: 305 | try: 306 | x_batch, y_batch = zip(*batch) 307 | loss, acc = discriminator(np.array(x_batch), y_batch, dis_dropout_keep_prob) 308 | if flag: 309 | print(float(acc.data)) 310 | flag=False 311 | dis_optimizer.zero_grads() 312 | loss.backward() 313 | dis_optimizer.update() 314 | sum_train_loss.append(float(loss.data)) 315 | sum_train_accuracy.append(float(acc.data)) 316 | except ValueError: 317 | pass 318 | 319 | dis_train_count += 1 320 | summary = sess.run(dis_loss_summary, feed_dict={loss_: np.mean(sum_train_loss)}) 321 | summary_writer.add_summary(summary, dis_train_count) 322 | summary = sess.run(dis_acc_summary, feed_dict={loss_: np.mean(sum_train_accuracy)}) 323 | summary_writer.add_summary(summary, dis_train_count) 324 | print(' dis-train step: {}/{} train_loss: {} train_acc: {}'.format(step+1, d_steps, np.mean(sum_train_loss), np.mean(sum_train_accuracy))) 325 | 326 | test_loss = generator.target_loss(target_lstm, 1000, gen_batch_size, sess) 327 | print(' test_loss: ', test_loss) 328 | test_count += 1 329 | summary = sess.run(target_loss_summary, feed_dict={loss_: test_loss}) 330 | summary_writer.add_summary(summary, test_count) 331 | -------------------------------------------------------------------------------- /oracle_test/utils_oracle.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from re import compile as _Re 3 | import tensorflow as tf 4 | 5 | 6 | class TARGET_LSTM(object): 7 | def __init__(self, num_emb, batch_size, emb_dim, hidden_dim, 8 | sequence_length, start_token, params): 9 | self.num_emb = num_emb 10 | self.batch_size = batch_size 11 | self.emb_dim = emb_dim 12 | self.hidden_dim = hidden_dim 13 | self.sequence_length = sequence_length 14 | self.start_token = tf.constant([start_token] * self.batch_size, dtype=tf.int32) 15 | self.g_params = [] 16 | self.temperature = 1.0 17 | self.params = params 18 | 19 | tf.set_random_seed(66) 20 | 21 | with tf.variable_scope('generator'): 22 | self.g_embeddings = tf.Variable(self.params[0]) 23 | self.g_params.append(self.g_embeddings) 24 | self.g_recurrent_unit = self.create_recurrent_unit(self.g_params) # maps h_tm1 to h_t for generator 25 | self.g_output_unit = self.create_output_unit(self.g_params) # maps h_t to o_t (output token logits) 26 | 27 | # placeholder definition 28 | self.x = tf.placeholder(tf.int32, shape=[self.batch_size, self.sequence_length]) 29 | # sequence of indices of true data, not including start token 30 | 31 | # processed for batch 32 | with tf.device("/cpu:0"): 33 | inputs = tf.split(1, self.sequence_length, tf.nn.embedding_lookup(self.g_embeddings, self.x)) 34 | self.processed_x = tf.pack( 35 | [tf.squeeze(input_, [1]) for input_ in inputs]) # seq_length x batch_size x emb_dim 36 | 37 | self.h0 = tf.zeros([self.batch_size, self.hidden_dim]) 38 | self.h0 = tf.pack([self.h0, self.h0]) 39 | 40 | # generator on initial randomness 41 | gen_o = tf.TensorArray(dtype=tf.float32, size=self.sequence_length, 42 | dynamic_size=False, infer_shape=True) 43 | gen_x = tf.TensorArray(dtype=tf.int32, size=self.sequence_length, 44 | dynamic_size=False, infer_shape=True) 45 | 46 | def _g_recurrence(i, x_t, h_tm1, gen_o, gen_x): 47 | h_t = self.g_recurrent_unit(x_t, h_tm1) # hidden_memory_tuple 48 | o_t = self.g_output_unit(h_t) # batch x vocab , logits not prob 49 | log_prob = tf.log(tf.nn.softmax(o_t)) 50 | next_token = tf.cast(tf.reshape(tf.multinomial(log_prob, 1), [self.batch_size]), tf.int32) 51 | x_tp1 = tf.nn.embedding_lookup(self.g_embeddings, next_token) # batch x emb_dim 52 | gen_o = gen_o.write(i, tf.reduce_sum(tf.mul(tf.one_hot(next_token, self.num_emb, 1.0, 0.0), 53 | tf.nn.softmax(o_t)), 1)) # [batch_size] , prob 54 | gen_x = gen_x.write(i, next_token) # indices, batch_size 55 | return i + 1, x_tp1, h_t, gen_o, gen_x 56 | 57 | _, _, _, self.gen_o, self.gen_x = tf.while_loop( 58 | cond=lambda i, _1, _2, _3, _4: i < self.sequence_length, 59 | body=_g_recurrence, 60 | loop_vars=(tf.constant(0, dtype=tf.int32), 61 | tf.nn.embedding_lookup(self.g_embeddings, self.start_token), self.h0, gen_o, gen_x)) 62 | 63 | self.gen_x = self.gen_x.pack() # seq_length x batch_size 64 | self.gen_x = tf.transpose(self.gen_x, perm=[1, 0]) # batch_size x seq_length 65 | 66 | # supervised pretraining for generator 67 | g_predictions = tf.TensorArray( 68 | dtype=tf.float32, size=self.sequence_length, 69 | dynamic_size=False, infer_shape=True) 70 | 71 | ta_emb_x = tf.TensorArray( 72 | dtype=tf.float32, size=self.sequence_length) 73 | ta_emb_x = ta_emb_x.unpack(self.processed_x) 74 | 75 | def _pretrain_recurrence(i, x_t, h_tm1, g_predictions): 76 | h_t = self.g_recurrent_unit(x_t, h_tm1) 77 | o_t = self.g_output_unit(h_t) 78 | g_predictions = g_predictions.write(i, tf.nn.softmax(o_t)) # batch x vocab_size 79 | x_tp1 = ta_emb_x.read(i) 80 | return i + 1, x_tp1, h_t, g_predictions 81 | 82 | _, _, _, self.g_predictions = tf.while_loop( 83 | cond=lambda i, _1, _2, _3: i < self.sequence_length, 84 | body=_pretrain_recurrence, 85 | loop_vars=(tf.constant(0, dtype=tf.int32), 86 | tf.nn.embedding_lookup(self.g_embeddings, self.start_token), 87 | self.h0, g_predictions)) 88 | 89 | self.g_predictions = tf.transpose( 90 | self.g_predictions.pack(), perm=[1, 0, 2]) # batch_size x seq_length x vocab_size 91 | 92 | # pretraining loss 93 | self.pretrain_loss = -tf.reduce_sum( 94 | tf.one_hot(tf.to_int32(tf.reshape(self.x, [-1])), self.num_emb, 1.0, 0.0) * tf.log( 95 | tf.reshape(self.g_predictions, [-1, self.num_emb]))) / (self.sequence_length * self.batch_size) 96 | 97 | self.pretrain_summary = tf.scalar_summary('test_loss', self.pretrain_loss) 98 | 99 | self.out_loss = tf.reduce_sum( 100 | tf.reshape( 101 | -tf.reduce_sum( 102 | tf.one_hot(tf.to_int32(tf.reshape(self.x, [-1])), self.num_emb, 1.0, 0.0) * tf.log( 103 | tf.reshape(self.g_predictions, [-1, self.num_emb])), 1 104 | ), [-1, self.sequence_length] 105 | ), 1 106 | ) # batch_size 107 | 108 | def generate(self, session): 109 | # h0 = np.random.normal(size=self.hidden_dim) 110 | outputs = session.run([self.gen_x]) 111 | return outputs[0] 112 | 113 | @staticmethod 114 | def init_matrix(shape): 115 | return tf.random_normal(shape, stddev=1.0) 116 | 117 | def create_recurrent_unit(self, params): 118 | # Weights and Bias for input and hidden tensor 119 | self.Wi = tf.Variable(self.params[1]) 120 | self.Ui = tf.Variable(self.params[2]) 121 | self.bi = tf.Variable(self.params[3]) 122 | 123 | self.Wf = tf.Variable(self.params[4]) 124 | self.Uf = tf.Variable(self.params[5]) 125 | self.bf = tf.Variable(self.params[6]) 126 | 127 | self.Wog = tf.Variable(self.params[7]) 128 | self.Uog = tf.Variable(self.params[8]) 129 | self.bog = tf.Variable(self.params[9]) 130 | 131 | self.Wc = tf.Variable(self.params[10]) 132 | self.Uc = tf.Variable(self.params[11]) 133 | self.bc = tf.Variable(self.params[12]) 134 | params.extend([ 135 | self.Wi, self.Ui, self.bi, 136 | self.Wf, self.Uf, self.bf, 137 | self.Wog, self.Uog, self.bog, 138 | self.Wc, self.Uc, self.bc]) 139 | 140 | def unit(x, hidden_memory_tm1): 141 | previous_hidden_state, c_prev = tf.unpack(hidden_memory_tm1) 142 | 143 | # Input Gate 144 | i = tf.sigmoid( 145 | tf.matmul(x, self.Wi) + 146 | tf.matmul(previous_hidden_state, self.Ui) + self.bi 147 | ) 148 | 149 | # Forget Gate 150 | f = tf.sigmoid( 151 | tf.matmul(x, self.Wf) + 152 | tf.matmul(previous_hidden_state, self.Uf) + self.bf 153 | ) 154 | 155 | # Output Gate 156 | o = tf.sigmoid( 157 | tf.matmul(x, self.Wog) + 158 | tf.matmul(previous_hidden_state, self.Uog) + self.bog 159 | ) 160 | 161 | # New Memory Cell 162 | c_ = tf.nn.tanh( 163 | tf.matmul(x, self.Wc) + 164 | tf.matmul(previous_hidden_state, self.Uc) + self.bc 165 | ) 166 | 167 | # Final Memory cell 168 | c = f * c_prev + i * c_ 169 | 170 | # Current Hidden state 171 | current_hidden_state = o * tf.nn.tanh(c) 172 | 173 | return tf.pack([current_hidden_state, c]) 174 | 175 | return unit 176 | 177 | def create_output_unit(self, params): 178 | self.Wo = tf.Variable(self.params[13]) 179 | self.bo = tf.Variable(self.params[14]) 180 | params.extend([self.Wo, self.bo]) 181 | 182 | def unit(hidden_memory_tuple): 183 | hidden_state, c_prev = tf.unpack(hidden_memory_tuple) 184 | # hidden_state : batch x hidden_dim 185 | logits = tf.matmul(hidden_state, self.Wo) + self.bo 186 | # output = tf.nn.softmax(logits) 187 | return logits 188 | 189 | return unit 190 | 191 | 192 | class Gen_Data_loader(): 193 | def __init__(self, batch_size): 194 | self.batch_size = batch_size 195 | self.token_stream = [] 196 | 197 | def create_batches(self, data_file): 198 | self.token_stream = [] 199 | with open(data_file, 'r') as f: 200 | for line in f: 201 | line = line.strip() 202 | line = line.split() 203 | parse_line = [int(x) for x in line] 204 | if len(parse_line) == 20: 205 | self.token_stream.append(parse_line) 206 | 207 | self.num_batch = int(len(self.token_stream) / self.batch_size) 208 | self.token_stream = self.token_stream[:self.num_batch * self.batch_size] 209 | self.sequence_batch = np.split(np.array(self.token_stream), self.num_batch, 0) 210 | self.pointer = 0 211 | 212 | def next_batch(self): 213 | ret = self.sequence_batch[self.pointer] 214 | self.pointer = (self.pointer + 1) % self.num_batch 215 | return ret 216 | 217 | def reset_pointer(self): 218 | self.pointer = 0 219 | 220 | 221 | class Likelihood_data_loader(): 222 | def __init__(self, batch_size): 223 | self.batch_size = batch_size 224 | self.token_stream = [] 225 | 226 | def create_batches(self, data_file): 227 | self.token_stream = [] 228 | with open(data_file, 'r') as f: 229 | for line in f: 230 | line = line.strip() 231 | line = line.split() 232 | parse_line = [int(x) for x in line] 233 | if len(parse_line) == 20: 234 | self.token_stream.append(parse_line) 235 | 236 | self.num_batch = int(len(self.token_stream) / self.batch_size) 237 | self.token_stream = self.token_stream[:self.num_batch * self.batch_size] 238 | self.sequence_batch = np.split(np.array(self.token_stream), self.num_batch, 0) 239 | self.pointer = 0 240 | 241 | def next_batch(self): 242 | ret = self.sequence_batch[self.pointer] 243 | self.pointer = (self.pointer + 1) % self.num_batch 244 | return ret 245 | 246 | def reset_pointer(self): 247 | self.pointer = 0 248 | 249 | 250 | def split_unicode_chrs(text): 251 | _unicode_chr_splitter = _Re('(?s)((?:[\ud800-\udbff][\udc00-\udfff])|.)').split 252 | return [chr for chr in _unicode_chr_splitter(text) if chr] 253 | 254 | 255 | class Dis_dataloader(): 256 | def __init__(self): 257 | self.vocab_size = 5000 258 | 259 | def load_data_and_labels(self, positive_file, negative_file): 260 | """ 261 | Loads MR polarity data from files, splits the data into words and generates labels. 262 | Returns split sentences and labels. 263 | """ 264 | positive_examples = [] 265 | negative_examples = [] 266 | with open(positive_file)as fin: 267 | for line in fin: 268 | line = line.strip() 269 | line = line.split() 270 | parse_line = [int(x) for x in line] 271 | positive_examples.append(parse_line) 272 | 273 | with open(negative_file)as fin: 274 | for line in fin: 275 | line = line.strip() 276 | line = line.split() 277 | parse_line = [int(x) for x in line] 278 | if len(parse_line) == 20: 279 | negative_examples.append(parse_line) 280 | 281 | # Split by words 282 | x_text = positive_examples + negative_examples 283 | 284 | # Generate labels 285 | positive_labels = [1 for _ in positive_examples] 286 | negative_labels = [0 for _ in negative_examples] 287 | y = positive_labels + negative_labels 288 | 289 | x_text = np.array(x_text) 290 | y = np.array(y) 291 | return [x_text, y] 292 | 293 | def load_train_data(self, positive_file, negative_file): 294 | """ 295 | Returns input vectors, labels, vocabulary, and inverse vocabulary. 296 | """ 297 | # Load and preprocess data 298 | sentences, labels = self.load_data_and_labels(positive_file, negative_file) 299 | shuffle_indices = np.random.permutation(np.arange(len(labels))) 300 | x_shuffled = sentences[shuffle_indices] 301 | y_shuffled = labels[shuffle_indices] 302 | self.sequence_length = 20 303 | return [x_shuffled, y_shuffled] 304 | 305 | def load_test_data(self, positive_file, test_file): 306 | test_examples = [] 307 | test_labels = [] 308 | with open(test_file)as fin: 309 | for line in fin: 310 | line = line.strip() 311 | line = line.split() 312 | parse_line = [int(x) for x in line] 313 | test_examples.append(parse_line) 314 | test_labels.append([1, 0]) 315 | 316 | with open(positive_file)as fin: 317 | for line in fin: 318 | line = line.strip() 319 | line = line.split() 320 | parse_line = [int(x) for x in line] 321 | test_examples.append(parse_line) 322 | test_labels.append([0, 1]) 323 | 324 | test_examples = np.array(test_examples) 325 | test_labels = np.array(test_labels) 326 | shuffle_indices = np.random.permutation(np.arange(len(test_labels))) 327 | x_dev = test_examples[shuffle_indices] 328 | y_dev = test_labels[shuffle_indices] 329 | 330 | return [x_dev, y_dev] 331 | 332 | def batch_iter(self, data, batch_size, num_epochs): 333 | """ 334 | Generates a batch iterator for a dataset. 335 | """ 336 | data = np.array(list(data)) 337 | data_size = len(data) 338 | num_batches_per_epoch = int(len(data) / batch_size) + 1 339 | for epoch in range(num_epochs): 340 | # Shuffle the data at each epoch 341 | shuffle_indices = np.random.permutation(np.arange(data_size)) 342 | shuffled_data = data[shuffle_indices] 343 | for batch_num in range(num_batches_per_epoch): 344 | start_index = batch_num * batch_size 345 | end_index = min((batch_num + 1) * batch_size, data_size) 346 | yield shuffled_data[start_index:end_index] 347 | --------------------------------------------------------------------------------