Skip to content

Commit 15ba6df

Browse files
authored
Merge pull request #1384 from serakiepiphany/dev-postgresql
Add the dataset for imdb data
2 parents 3cbdd53 + 647cce5 commit 15ba6df

1 file changed

Lines changed: 283 additions & 0 deletions

File tree

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
# =============================================================================
17+
18+
import re
19+
import os
20+
import pickle
21+
import urllib
22+
import tarfile
23+
import numpy as np
24+
import pandas as pd
25+
import nltk
26+
from nltk.stem import PorterStemmer
27+
from nltk.tokenize.toktok import ToktokTokenizer
28+
from gensim.models.keyedvectors import KeyedVectors
29+
from sklearn.model_selection import train_test_split
30+
from bs4 import BeautifulSoup
31+
'''
32+
data collection preprocessing constants
33+
'''
34+
download_dir = '/tmp/'
35+
preprocessed_imdb_data_fp = download_dir + 'imdb_processed.pickle'
36+
imdb_dataset_link = "https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz"
37+
google_news_pretrain_embeddings_link = "https://s3.amazonaws.com/dl4j-distribution/GoogleNews-vectors-negative300.bin.gz"
38+
39+
40+
def pad_batch(b, seq_limit):
41+
''' convert a batch of encoded sequence
42+
to pretrained word vectors from the embed weights (lookup dictionary)
43+
'''
44+
batch_seq = []
45+
batch_senti_onehot = []
46+
batch_senti = []
47+
for r in b:
48+
# r[0] encoded sequence
49+
# r[1] label 1 or 0
50+
encoded = None
51+
if len(r[0]) >= seq_limit:
52+
encoded = r[0][:seq_limit]
53+
else:
54+
encoded = r[0] + [0] * (seq_limit - len(r[0]))
55+
56+
batch_seq.append(encoded)
57+
batch_senti.append(r[1])
58+
if r[1] == 1:
59+
batch_senti_onehot.append([0, 1])
60+
else:
61+
batch_senti_onehot.append([1, 0])
62+
batch_senti = np.array(batch_senti).astype(np.float32)
63+
batch_senti_onehot = np.array(batch_senti_onehot).astype(np.float32)
64+
batch_seq = np.array(batch_seq).astype(np.int32)
65+
return batch_seq, batch_senti_onehot, batch_senti
66+
67+
68+
def pad_batch_2vec(b, seq_limit, embed_weights):
69+
''' convert a batch of encoded sequence
70+
to pretrained word vectors from the embed weights (lookup dictionary)
71+
'''
72+
batch_seq = []
73+
batch_senti_onehot = []
74+
batch_senti = []
75+
for r in b:
76+
# r[0] encoded sequence
77+
# r[1] label 1 or 0
78+
encoded = None
79+
if len(r[0]) >= seq_limit:
80+
encoded = r[0][:seq_limit]
81+
else:
82+
encoded = r[0] + [0] * (seq_limit - len(r[0]))
83+
84+
batch_seq.append([embed_weights[idx] for idx in encoded])
85+
batch_senti.append(r[1])
86+
if r[1] == 1:
87+
batch_senti_onehot.append([0, 1])
88+
else:
89+
batch_senti_onehot.append([1, 0])
90+
batch_senti = np.array(batch_senti).astype(np.float32)
91+
batch_senti_onehot = np.array(batch_senti_onehot).astype(np.float32)
92+
batch_seq = np.array(batch_seq).astype(np.float32)
93+
return batch_seq, batch_senti_onehot, batch_senti
94+
95+
96+
def check_exist_or_download(url):
97+
''' download data into tmp '''
98+
name = url.rsplit('/', 1)[-1]
99+
filename = os.path.join(download_dir, name)
100+
if not os.path.isfile(filename):
101+
print("Downloading %s" % url)
102+
urllib.request.urlretrieve(url, filename)
103+
return filename
104+
105+
106+
def unzip_data(download_dir, data_gz):
107+
data_dir = download_dir + 'aclImdb'
108+
if not os.path.exists(data_dir):
109+
print("extracting %s to %s" % (download_dir, data_dir))
110+
with tarfile.open(data_gz) as tar:
111+
tar.extractall(download_dir)
112+
return data_dir
113+
114+
115+
def strip_html(text):
116+
''' lambda fn for cleaning html '''
117+
soup = BeautifulSoup(text, "html.parser")
118+
return soup.get_text()
119+
120+
121+
def remove_between_square_brackets(text):
122+
''' lambda fn for cleaning square brackets'''
123+
return re.sub('\[[^]]*\]', '', text)
124+
125+
126+
def remove_special_characters(text, remove_digits=True):
127+
''' lambda fn for removing special char '''
128+
pattern = r'[^a-zA-Z0-9\s]'
129+
text = re.sub(pattern, '', text)
130+
return text
131+
132+
133+
def simple_stemmer(text):
134+
''' lambda fn for stemming '''
135+
ps = PorterStemmer()
136+
text = ' '.join([ps.stem(word) for word in text.split()])
137+
return text
138+
139+
140+
def remove_stopwords(text, tokenizer, stopword_list, is_lower_case=False):
141+
''' lambda fn for removing stopwrods '''
142+
tokens = tokenizer.tokenize(text)
143+
tokens = [token.strip() for token in tokens]
144+
if is_lower_case:
145+
filtered_tokens = [
146+
token for token in tokens if token not in stopword_list
147+
]
148+
else:
149+
filtered_tokens = [
150+
token for token in tokens if token.lower() not in stopword_list
151+
]
152+
filtered_text = ' '.join(filtered_tokens)
153+
return filtered_text
154+
155+
156+
def tokenize(x):
157+
''' lambda fn for tokenize sentences '''
158+
ret = []
159+
for w in x.split(" "):
160+
if w != '':
161+
ret.append(w)
162+
return ret
163+
164+
165+
def encode_token(words, wv, w2i):
166+
''' lambda fn for encoding string seq to int seq
167+
args:
168+
wv: word vector lookup dictionary
169+
w2i: word2index lookup dictionary
170+
'''
171+
ret = []
172+
for w in words:
173+
if w in wv:
174+
ret.append(w2i[w])
175+
return ret
176+
177+
178+
def preprocess():
179+
''' collect and preprocess raw data from acl Imdb dataset
180+
'''
181+
nltk.download('stopwords')
182+
183+
print("preparing raw imdb data")
184+
data_gz = check_exist_or_download(imdb_dataset_link)
185+
data_dir = unzip_data(download_dir, data_gz)
186+
187+
# imdb dirs
188+
# vocab_f = data_dir + '/imdb.vocab'
189+
train_pos_dir = data_dir + '/train/pos/'
190+
train_neg_dir = data_dir + '/train/neg/'
191+
test_pos_dir = data_dir + '/test/pos/'
192+
test_neg_dir = data_dir + '/test/neg/'
193+
194+
# nltk helpers
195+
tokenizer = ToktokTokenizer()
196+
stopword_list = nltk.corpus.stopwords.words('english')
197+
198+
# load pretrained word2vec binary
199+
print("loading pretrained word2vec")
200+
google_news_pretrain_fp = check_exist_or_download(
201+
google_news_pretrain_embeddings_link)
202+
wv = KeyedVectors.load_word2vec_format(google_news_pretrain_fp, binary=True)
203+
204+
# parse flat files to memory
205+
data = []
206+
for data_dir, label in [(train_pos_dir, 1), (train_neg_dir, 0),
207+
(test_pos_dir, 1), (test_neg_dir, 0)]:
208+
for filename in os.listdir(data_dir):
209+
if filename.endswith(".txt"):
210+
with open(os.path.join(data_dir, filename),
211+
"r",
212+
encoding="utf-8") as fhdl:
213+
data.append((fhdl.read(), label))
214+
215+
# text review cleaning
216+
print("cleaning text review")
217+
imdb_data = pd.DataFrame(data, columns=["review", "label"])
218+
imdb_data['review'] = imdb_data['review'].apply(strip_html)
219+
imdb_data['review'] = imdb_data['review'].apply(
220+
remove_between_square_brackets)
221+
imdb_data['review'] = imdb_data['review'].apply(remove_special_characters)
222+
imdb_data['review'] = imdb_data['review'].apply(simple_stemmer)
223+
imdb_data['review'] = imdb_data['review'].apply(remove_stopwords,
224+
args=(tokenizer,
225+
stopword_list))
226+
imdb_data['token'] = imdb_data['review'].apply(tokenize)
227+
228+
# build word2index and index2word
229+
w2i = dict()
230+
i2w = dict()
231+
232+
# add vocab <pad> as index 0
233+
w2i["<pad>"] = 0
234+
i2w[0] = "<pad>"
235+
236+
idx = 1 # start from idx 1
237+
for index, row in imdb_data['token'].iteritems():
238+
for w in row:
239+
if w in wv and w not in w2i:
240+
w2i[w] = idx
241+
i2w[idx] = w
242+
assert idx < 28241
243+
idx += 1
244+
assert len(w2i) == len(i2w)
245+
print("vocab size: ", len(w2i))
246+
247+
# encode tokens to int
248+
imdb_data['encoded'] = imdb_data['token'].apply(encode_token,
249+
args=(wv, w2i))
250+
251+
# select word vector weights for embedding layer from vocab
252+
embed_weights = []
253+
for w in w2i.keys():
254+
val = None
255+
if w in wv:
256+
val = wv[w]
257+
else:
258+
val = np.zeros([
259+
300,
260+
])
261+
embed_weights.append(val)
262+
embed_weights = np.array(embed_weights)
263+
print("embedding layer lookup weight shape: ", embed_weights.shape)
264+
265+
# split into train and test
266+
train_data = imdb_data[['encoded', 'label']].values
267+
train, val = train_test_split(train_data, test_size=0.33, random_state=42)
268+
269+
# save preprocessed for training
270+
imdb_processed = {
271+
"train": train,
272+
"val": val,
273+
"embed_weights": embed_weights,
274+
"w2i": w2i,
275+
"i2w": i2w
276+
}
277+
print("saving preprocessed file to ", preprocessed_imdb_data_fp)
278+
with open(preprocessed_imdb_data_fp, 'wb') as handle:
279+
pickle.dump(imdb_processed, handle, protocol=pickle.HIGHEST_PROTOCOL)
280+
281+
282+
if __name__ == "__main__":
283+
preprocess()

0 commit comments

Comments
 (0)