<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          tf.keras文本分類小例子

          共 12528字,需瀏覽 26分鐘

           ·

          2021-01-12 02:04

          前幾天學(xué)了tf.keras,趁熱打鐵我就整了一個自己比較熟悉的文本分類任務(wù)來試試手,效果可能不是很重要,重要的是能把流程走通,所以一切從簡。

          當(dāng)然,我這里不想只是簡單的弄個流程,碼面條那么簡單,我還是希望能整一次完整的工程化的代碼來練練手,2天時間,純手打歡迎各位前輩拍磚,也希望對各位有所幫助吧。

          懶人目錄:

          • 文件結(jié)構(gòu)和模塊劃分思路
          • 預(yù)訓(xùn)練
          • 分類模型
          • MAIN
            • 預(yù)處理部分
            • word2vector
            • 建模操作
          • 小結(jié)
            • 后續(xù)改進計劃
            • 思路小結(jié)

          文件結(jié)構(gòu)和模塊劃分思路

          先聊聊整套方案的算法視角思路,文本分類任務(wù)的常規(guī)基線是TextCNN,這里為了簡單我只用了一個簡單的卷積層,而沒有用TextCNN里面那種復(fù)雜形式(有關(guān)這個模型的具體解釋詳見:NLP.TM[24] | TextCNN的個人理解)。

          先來看核心代碼的文件夾結(jié)構(gòu):

          • cls,文本分類模型,以及具體的任務(wù)實驗。
          • ptm,預(yù)訓(xùn)練模型。
          • util,工具。

          這里面的base_pipline.py是一套完整的流程化代碼,接近180行,覆蓋加載數(shù)據(jù)、預(yù)處理、訓(xùn)練、測試等全流程,當(dāng)然我不滿足于此,我把里面的關(guān)鍵步驟模塊化,形成一個個分別的模塊來分別實現(xiàn)。

          預(yù)訓(xùn)練

          預(yù)訓(xùn)練我單獨拉出來,沒有和整體模型放一起,主要是因為預(yù)訓(xùn)練模型需要單獨訓(xùn)練,也完整地維護了起來,這里我只寫了個最簡單的word2vector,模型部分使用的也只是調(diào)了gensim的包,來看看完整的類代碼。

          import?numpy?as?np
          from?nlu_model.util.pkl_impl?import?save_pkl,?load_pkl
          from?gensim.models.word2vec?import?Word2Vec

          class?Word2vector(object):
          ????def?__init__(self):
          ????????self.word2idx_dic?=?{}
          ????????self.embedding_weights?=?[]

          ????def?train(self,?
          ??????????????train_data,??????????????????????????#?訓(xùn)練數(shù)據(jù)
          ??????????????N_DIM?=?300,?????????????????????????#?word2vec的數(shù)量
          ??????????????MIN_COUNT?=?5,???????????????????????#?保證出現(xiàn)的詞數(shù)足夠做才進入詞典
          ??????????????w2v_EPOCH?=?15,??????????????????????#?w2v的訓(xùn)練迭代次數(shù)
          ??????????????MAXLEN?=?50??????????????????????????#?句子最大長度
          ??????????????)
          :


          ????????self.N_DIM?=?N_DIM
          ????????self.MIN_COUNT?=?MIN_COUNT
          ????????self.w2v_EPOCH?=?w2v_EPOCH
          ????????self.MAXLEN?=?MAXLEN

          ????????#?Initialize?model?and?build?vocab
          ????????imdb_w2v?=?Word2Vec(size=N_DIM,?min_count=MIN_COUNT)
          ????????imdb_w2v.build_vocab(train_data)

          ????????#?Train?the?model?over?train_reviews?(this?may?take?several?minutes)
          ????????imdb_w2v.train(train_data,?total_examples=len(train_data),?epochs=w2v_EPOCH)

          ????????#?word2vec后處理
          ????????n_symbols?=?len(imdb_w2v.wv.vocab.keys())?+?2
          ????????embedding_weights?=?[[0?for?i?in?range(N_DIM)]?for?i?in?range(n_symbols)]
          ????????np.zeros((n_symbols,?300))
          ????????idx?=?1
          ????????word2idx_dic?=?{}
          ????????w2v_model_metric?=?[]
          ????????for?w?in?imdb_w2v.wv.vocab.keys():
          ????????????embedding_weights[idx]?=?imdb_w2v[w]
          ????????????word2idx_dic[w]?=?idx
          ????????????idx?=?idx?+?1

          ????????#?留給未登錄詞的位置
          ????????avg_weights?=?[0?for?i?in?range(N_DIM)]
          ????????for?wd?in?word2idx_dic:
          ????????????avg_weights?=?[(avg_weights[idx]+embedding_weights[word2idx_dic[wd]][idx])?for?idx?in?range(N_DIM)]
          ????????avg_weights?=?[avg_weights[idx]?/?len(word2idx_dic)?for?idx?in?range(N_DIM)]
          ????????embedding_weights[idx]?=?avg_weights
          ????????word2idx_dic[""]?=?idx

          ????????#?留給pad的位置
          ????????word2idx_dic[""]?=?0

          ????????self.word2idx_dic?=?word2idx_dic
          ????????self.embedding_weights?=?embedding_weights

          ????def?save(self,
          ?????????????word2idx_dic_path,???????????????????#?詞到ID詞典路徑
          ?????????????embedding_path,??????????????????????#?embedding詞向量路徑
          ?????????????model_conf_path?????????????????????#?模型配置加載)

          ?????????????):

          ??????
          ????????#?保存w2id詞典
          ????????save_pkl(word2idx_dic_path,?self.word2idx_dic)
          ????????#?保存詞向量矩陣
          ????????save_pkl(embedding_path,?self.embedding_weights)
          ????????#?保存配置
          ????????save_pkl(model_conf_path,?[self.N_DIM,?self.MIN_COUNT,?self.w2v_EPOCH,?self.MAXLEN])

          ????def?__load_default__(self):
          ????????self.load("./data/ptm/shopping_reviews/w2v_word2idx2020100601.pkl",
          ??????????????????"./data/ptm/shopping_reviews/w2v_model_metric_2020100601.pkl",?
          ??????????????????"./data/ptm/shopping_reviews/w2v_model_conf_2020100601.pkl")

          ????def?load(self,?word2idx_dic_path,?embedding_path,?model_conf_path):
          ????????self.N_DIM,?self.MIN_COUNT,?self.w2v_EPOCH,?self.MAXLEN?=?load_pkl(model_conf_path)
          ????????self.embedding_weights?=?load_pkl(embedding_path)
          ????????self.word2idx_dic?=?load_pkl(word2idx_dic_path)


          ????def?word2idx(self,?word):
          ????????if?len(self.word2idx_dic)?==?0:
          ????????????self.__load_default__()
          ????????if?word?in?self.word2idx_dic:
          ????????????return?self.word2idx_dic[word]
          ????????else:
          ????????????return?len(self.word2idx_dic)?-?1

          ????def?sentence2idx(self,?sentence):
          ????????sentence_idx?=?[]
          ????????for?idx?in?range(len(sentence)):
          ????????????sentence_idx.append(self.word2idx(sentence[idx]))
          ????????return?sentence_idx

          ????def?batch2idx(self,?source_data):
          ????????result_data?=?[]
          ????????for?idx?in?range(len(source_data)):
          ????????????result_data.append(self.sentence2idx(source_data[idx]))
          ????????return?result_data

          ????def?get_np_weights(self):
          ????????return?np.array(self.embedding_weights)

          這里占比最大的是模型訓(xùn)練過程中的數(shù)據(jù)處理,剩下都是圍繞著這個訓(xùn)練完的word2vector做的一些操作,我來畫幾個重點吧:

          • 詞匯的id化。tensorflow在訓(xùn)練過程中embedding_lookup本身是數(shù)字計算,所以所有的詞匯都要轉(zhuǎn)化為id,而這個id的生成其實來源于word2vector的訓(xùn)練,因此我把映射詞典也放在這個類里面維護了,那就包括了各種粒度的映射了。
          • 模型和映射詞典以及一些必要的參數(shù)保存,我用的是pkl來進行保存,這個保存和加載都比較簡單,來看具體的pkl_impl我是怎么寫的:
          import?pickle

          def?save_pkl(path,?data):
          ?output?=?open(path,?'wb')
          ?pickle.dump(data,?output)
          ?output.close()

          def?load_pkl(path):
          ?pkl_file?=?open(path,?'rb')
          ?data?=?pickle.load(pkl_file)
          ?pkl_file.close()
          ?return?data
          • 在詞典部分,我手動加了兩個特殊詞條:未登錄詞,對應(yīng)的詞向量是所有詞向量的均值,pad補全,對應(yīng)詞向量全都是0。

          當(dāng)然,后續(xù)還可能有更多預(yù)訓(xùn)練的模型,自己可以再調(diào)整,這也是模塊化的好處,后續(xù)要更新模型,就和換零件一樣。

          分類模型

          分類模型這塊是分了兩層,一個cls模型類,一個具體的模型也把他整成一個類了(這個后續(xù)會整一個抽象類讓他繼承吧)。

          首先看具體模型的類,textcnn_small,畢竟這個不是正兒八經(jīng)的那個textcnn。

          from?tensorflow?import?keras

          class?TextCNNSmall():
          ????"""docstring?for?TextCNNSmall"""
          ????def?__init__(self,?model_conf,?train_conf={"batch_size":64,"epochs":3,?"verbose":1}):
          ????????self.model_conf?=?model_conf
          ????????self.train_conf?=?train_conf
          ????????self.__build_structure__()


          ????def?__build_structure__(self):
          ????????inputs?=?keras.layers.Input(shape=(self.model_conf["MAX_LEN"],))
          ????????embedding_layer?=?keras.layers.Embedding(output_dim?=?self.model_conf["w2c_len"],
          ????????????????????????????????????input_dim?=?len(self.model_conf["emb_model"].embedding_weights),?
          ????????????????????????????????????weights=[self.model_conf["emb_model"].get_np_weights()],?
          ????????????????????????????????????input_length=self.model_conf["MAX_LEN"],?
          ????????????????????????????????????trainable=True
          ????????????????????????????????????)
          ????????x?=?embedding_layer(inputs)

          ????????l_conv1?=?keras.layers.Conv1D(filters=self.model_conf["w2c_len"],?kernel_size=3,?activation='relu')(x)??
          ????????l_pool1?=?keras.layers.MaxPool1D(pool_size=3)(l_conv1)
          ????????l_pool11?=?keras.layers.Flatten()(l_pool1)

          ????????out?=?keras.layers.Dropout(0.5)(l_pool11)
          ????????output?=?keras.layers.Dense(32,?activation='relu')(out)
          ?????????
          ????????pred?=?keras.layers.Dense(units=1,?activation='sigmoid')(output)
          ?????????
          ????????self.model?=?keras.models.Model(inputs=inputs,?outputs=pred)
          ????????self.model.summary()
          ????????self.model.compile(loss="binary_crossentropy",?optimizer="adam",?metrics=['accuracy'])

          ????def?fit(self,?x_train,?y_train,?x_test,?y_test):
          ????????history?=?self.model.fit(x_train,?y_train,?batch_size=self.train_conf.get("batch_size",?64),
          ????????????????????????????????epochs=self.train_conf.get("epochs",?3),
          ????????????????????????????????validation_data=(x_test,?y_test),
          ????????????????????????????????verbose=self.train_conf.get("verbose",?1))
          ????????return?history

          ????def?evaluate(self,?x_test,?y_test):
          ????????return?self.model.evaluate(x_test,?y_test)

          ????def?predict(self,?sentences):
          ????????return?self.model.predict(sentences)

          ????def?save(self,?path):
          ????????if?self.model:
          ????????????self.model.save(path)

          ????def?load(self,?path):
          ????????self.model?=?keras.load_model(path)

          就科研而言模型還是核心,但實際上我還做了很多模型法之外的事情:

          • 簡單的模型構(gòu)建,這塊沒什么難的了。
          • fit、evaluate、predict,這是經(jīng)典的3個模型關(guān)鍵步驟,訓(xùn)練、評估、預(yù)測,對于工程而言,最關(guān)鍵的應(yīng)該就是預(yù)測了。
          • 模型的加載和保存,這塊我也涉及到了。
          • 這里面的參數(shù)我都配置化了,從外面輸入進來,當(dāng)然這種字典的形式只是個權(quán)宜之計,后續(xù)我會匯總好形成各種接口來往外直接暴露。

          模型類之上我還整了一個模型類,用來對各種同類型的模型進行維護。

          import?jieba
          from?tensorflow?import?keras
          from?nlu_model.cls.model.textcnn_small?import?TextCNNSmall

          class?ClsModel(object):
          ????def?__init__(self,?model_choice,?model_conf={},?train_conf={}):
          ????????self.model_choice?=?model_choice
          ????????self.model_conf?=?model_conf
          ????????self.train_conf?=?train_conf
          ????????self.__model_select__()

          ????def?__model_select__(self):
          ????????if?self.model_choice?==?"textcnn_small":
          ????????????self.model?=?TextCNNSmall(self.model_conf,?self.train_conf)
          ????????if?self.model_choice?==?"load":
          ????????????self.load(self.model_conf["path"])

          ????def?preprocess(self,?sentences):
          ????????sentences?=?[list(jieba.cut(i))?for?i?in?sentences]
          ????????sentence_id?=?self.model_conf["emb_model"].batch2idx(sentences)
          ????????return?keras.preprocessing.sequence.pad_sequences(sentence_id,
          ??????????????????????????????????????????????????????????value=0,
          ??????????????????????????????????????????????????????????padding='post',
          ??????????????????????????????????????????????????????????maxlen=50)

          ????def?fit(self,?x_train,?y_train,?x_test,?y_test):
          ????????return?self.model.fit(x_train,?y_train,?x_test,?y_test)

          ????def?evaluate(self,?x_test,?y_test):
          ????????return?self.model.evaluate(x_test,?y_test)

          ????def?pred(self,?sentence):
          ????????return?self.model.predict(sentence)

          ????def?predict(self,?sentences):
          ????????sentence_id?=?self.preprocess(sentences)
          ????????return?self.pred([sentence_id])[0][0]

          ????def?save(self,?path):
          ????????self.model.save(path)

          ????def?load(self,?path):
          ????????self.model?=?keras.models.load_model(path)

          同樣畫畫重點。

          • 這里的模型首先由__model_select__來進行統(tǒng)一維護和選擇,目前支持兩種模式,textcnn_small即一個具體的模型,另外的load是加載模式,維護具體的一個模型。
          • __用來區(qū)分是private類型成員還是public類型成員,即外界是否能讀到,一般不需要外界讀的盡量整成__,這里我還需要優(yōu)化。
          • 預(yù)處理的這塊工作具有一定的重用性,但是僅在文本分類中使用,因此我也維護在這里了,后續(xù)可以嘗試看看能不能放在util里面。
          • 訓(xùn)練、評估、預(yù)測3連,但是這里我整了兩個預(yù)測,一個是直接針對預(yù)處理好的id化序列進行預(yù)測,另一個是針對原句來進行預(yù)測,其實可以看到后者調(diào)用了前者的那個函數(shù)。

          MAIN

          有了這些模塊,我們就需要把他們給串起來了,這里我用的是網(wǎng)上找的一套電商評論好壞評的分類數(shù)據(jù)(https://blog.csdn.net/churximi/article/details/61210129)

          預(yù)處理部分

          首先是預(yù)處理部分:

          #?data?preprocess
          def?loadfile():
          ????#?加載并預(yù)處理模型
          ????neg?=?pd.read_excel('./data/cls/shopping_reviews/neg.xls',?header=None,?index=None)
          ????pos?=?pd.read_excel('./data/cls/shopping_reviews/pos.xls',?header=None,?index=None)

          ????def?cw(x):?
          ????????punctuation?=?r"[\s+\.\!\/_,$%^*(+\"\']+|[+——!,。?、~@#¥%……&*():]+"
          ????????x?=?re.sub(punctuation,?"",?x)

          ????????return?list(jieba.cut(x))
          ????pos['words']?=?pos[0].apply(cw)
          ????neg['words']?=?neg[0].apply(cw)

          ????y?=?np.concatenate((np.ones(len(pos)),?np.zeros(len(neg))))

          ????x_train,?x_test,?y_train,?y_test?=?train_test_split(
          ????????np.concatenate((pos['words'],?neg['words'])),?y,?test_size=0.2)
          ????
          ????return?x_train,?x_test,?y_train,?y_test

          x_train,?x_test,?y_train,?y_test?=?loadfile()
          with?open("./data/cls/shopping_reviews/train.txt",?"w")?as?f:
          ????for?idx?in?range(len(x_train)):
          ????????f.write("%s\t%s\n"?%?(y_train[idx],?"?".join(x_train[idx])))

          with?open("./data/cls/shopping_reviews/test.txt",?"w")?as?f:
          ????for?idx?in?range(len(x_test)):
          ????????f.write("%s\t%s\n"?%?(y_test[idx],?"?".join(x_test[idx])))

          源文件在excel里面,我去了標(biāo)點后切詞,切完之后做了數(shù)據(jù)劃分,然后分別保存起來了。

          word2vector

          word2vector?=?Word2vector()

          word2vector.train(x_train)
          word2vector.save("./data/ptm/shopping_reviews/w2v_word2idx2020100601.pkl",
          ?????????????????"./data/ptm/shopping_reviews/w2v_model_metric_2020100601.pkl",?
          ?????????????????"./data/ptm/shopping_reviews/w2v_model_conf_2020100601.pkl")

          然后是Word2vector的訓(xùn)練,可以看到這塊代碼非常簡潔方便,后續(xù)自己再用的時候就很舒服,所以我本身非常喜歡去包裝這些東西。

          建模操作

          建模這塊,主要有這幾個細(xì)節(jié)步驟:

          • 訓(xùn)練測試數(shù)據(jù)的結(jié)構(gòu)轉(zhuǎn)化,要使其符合模型需要的類型。
          • 模型建立和初始化。
          • 訓(xùn)練、評估。
          • 單獨測試case。

          然后來看看代碼:

          #?訓(xùn)練測試數(shù)據(jù)的結(jié)構(gòu)轉(zhuǎn)化,要使其符合模型需要的類型。
          x_train?=?word2vector.batch2idx(x_train)
          x_test?=?word2vector.batch2idx(x_test)

          x_train?=?keras.preprocessing.sequence.pad_sequences(x_train,
          ????????????????????????????????????????????????????value=0,
          ????????????????????????????????????????????????????padding='post',
          ????????????????????????????????????????????????????maxlen=50)
          x_test?=?keras.preprocessing.sequence.pad_sequences(x_test,
          ????????????????????????????????????????????????????value=0,
          ????????????????????????????????????????????????????padding='post',
          ????????????????????????????????????????????????????maxlen=50)

          #?模型建立和初始化
          model_conf?=?{"MAX_LEN":?50,
          ??????????????"w2c_len":?300,?
          ??????????????"emb_model":?word2vector}
          train_conf?=?{"batch_size":?64,
          ??????????????"epochs":?5,?
          ??????????????"verbose":?1}
          cls_model?=?ClsModel("textcnn_small",?model_conf,?train_conf)

          #?訓(xùn)練、評估
          cls_model.fit(x_train,?y_train,?x_test,?y_test)
          print(cls_model.evaluate(x_test,?y_test))
          cls_model.save("./data/cls/shopping_reviews/model_20201007")

          #?單獨測試case,這里包括用新訓(xùn)練好的模型和保存加載后的模型
          sentence?=?"這臺手機真性能還挺好的"
          print(cls_model.predict([sentence]))

          sentence?=?"這臺手機真性能還挺好的"
          word2vector?=?Word2vector()
          cls_model?=?ClsModel("load",?model_conf={"path":"./data/cls/shopping_reviews/model_20201007",?"emb_model":?word2vector},?train_conf={})
          print(cls_model.predict([sentence]))

          小結(jié)

          后續(xù)改進計劃

          這只是初步建立的一個架構(gòu),細(xì)節(jié)還不太完整,需要完善。

          • 注釋太少。(為了在今晚發(fā)問,所以省了些大家不會介意吧)
          • 在一個超大的項目下,其實還缺少打包、部署之類的框架類腳本和模塊,例如用flask等。
          • 同樣是大項目下,有多個模型和詞典時,需要一個model_manager之類的來維護,有興趣的可以去看看jieba的源碼。
          • 沒有日志、沒有耗時計算工具。
          • 針對單字的操作可以做。

          時候未到,尚未開源,敬請期待。

          思路小結(jié)

          會做實驗寫算法是一方面,會把自己的東西規(guī)范化、結(jié)構(gòu)化整上線又是另一回事,真正的算法工程師,要足夠全面,了解工程,最終才能夠完成這個項目,有了這個框架,自己嘗試更多的模型其實會更加快哈哈哈。

          瀏覽 57
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人精品视频99在线观看免费 | 香蕉伊| 日本黄视频在线观看网 | 7799天天综合网精品 | 国内毛片毛片毛片毛片毛片毛片毛片毛片 |