解決多個Keras Model實例同時加載枝誊,導(dǎo)致運行結(jié)果混亂的問題

最近做的一項任務(wù)顽聂,需要同時運行多個二分類模型(如果這些二分類結(jié)果穩(wěn)定了,即可轉(zhuǎn)為一個多標簽分類模型)么伯。

問題描述

假定分類模型分別為A和B
示例代碼如下:

# 將kerasutil_A 與kerasutil_B 作為全局變量疟暖,可以一次加載多次應(yīng)用,從而提升性能
kerasutil_A = KerasUtil(rawtextpath=r'A.csv',
                            lstmmodelpath=r'A.h5',
                            modeltype='bilstm')

kerasutil_B = KerasUtil(rawtextpath=r'B.csv',
                            lstmmodelpath=r'B.h5',
                            modeltype='bilstm')

具體的使用方式為:

resultlist = []
if categoryid == 'A':
    resultlist = kerasutil_A.predictcategory(sentence)
elif categoryid == 'B':
    resultlist = kerasutil_B.predictcategory(sentence)

但是在運行過程中,發(fā)現(xiàn)莫名奇妙的問題俐巴。
比如有兩個與類別B相關(guān)的句子:
sentence_B_1, Positive
sentence_B_2, Negative

在多個model實例加載完畢后骨望,
如果按照sentence_B_1, sentence_B_2的順序去跑,會得到sentence_B_1:Positive, sentence_B_2依然是Positive的結(jié)果欣舵;
如果按照sentence_B_2, sentence_B_1的順序去跑擎鸠,會得到sentence_B_2:Negative, sentence_B_1依然是Negative的結(jié)果;

但是如果只在一個進程加載Model B缘圈,則無論sentence_B_1, sentence_B_2按照什么順序去跑劣光,結(jié)果都是正確的。

事實上糟把,KerasUtil中沒有任何靜態(tài)或類級別的變量绢涡,都是實例級別的變量(每個實例中的屬性都是使用獨立的內(nèi)存地址):

class KerasUtil:
    def __init__(self, rawtextpath, lstmmodelpath, modeltype='lstm', multiplelabels=False):
        self.max_fatures = 2000
        self.modelrawtextpath = rawtextpath
        self.modelpath = lstmmodelpath
        self.modeltype = modeltype
        self.multiplelabels = multiplelabels
        self.data = None
        self.lstmmodel = None
        self.tokenizer = None
        self.initialtokenizer()

data,lstmmodel, tokenizer都是在實例化的時候糊饱,才去賦值垂寥,經(jīng)過跟蹤調(diào)試堆棧信息,也能夠得出kerasutil_A與kerasutil_B中的屬性都是獨立另锋,擁有各自特征的值滞项。

這確實是太奇怪了!夭坪!

目前尚不能確定是否是底層的Tensorflow對同一個進程的多個模型的應(yīng)用文判,導(dǎo)致出現(xiàn)類似問題。(模型內(nèi)的張量出現(xiàn)混亂室梅?)
不過事實證明戏仓,基于Tensorflow的Keras在多模型同時運行時,無論是單純的執(zhí)行predict方法(容易出現(xiàn)錯誤亡鼠,需要clear session或者重新加載模型)赏殃,還是執(zhí)行某個模型的分類結(jié)果,都是詭異的间涵。

比較消耗性能的解決方案

將模型的初始化與加載邏輯仁热,放置在應(yīng)用業(yè)務(wù)分類的函數(shù)內(nèi)。
每次函數(shù)執(zhí)行完畢勾哩,模型對象都會被釋放抗蠢。

def confirmwithmachinelearning(categoryid, sentencelist):
    # 用多個model時,出于性能考慮思劳,之前是作為全局變量加載的
    # 之后發(fā)現(xiàn)同時run多個文檔的的時候迅矛,結(jié)果居然與單獨去跑某個文檔的結(jié)果不一致
    # 猜測是否是底層的tensorflow的張量信息缺失或互相竄了
    # 目前這種方式是在函數(shù)內(nèi)加載對應(yīng)模型,雖然慢了幾秒潜叛,但結(jié)果是準確的
    kerasutil = None
    if categoryid == 'A':
        kerasutil = KerasUtil(rawtextpath=r'A.csv',
                              lstmmodelpath=r'A.h5',
                              modeltype='bilstm')
    if categoryid == 'B':
        kerasutil = KerasUtil(rawtextpath=r'B.csv',
                              lstmmodelpath=r'B.h5',
                              modeltype='bilstm')

    for sentence in sentencelist:
        resultlist = kerasutil.predictcategory(sentence)
    ...

經(jīng)過驗證秽褒,詭異的現(xiàn)象消失了壶硅,類別結(jié)果不會因為多模型加載而造成不同的結(jié)果。
附注:這種方式對于相對較小的模型震嫉,不會帶來性能方面的明顯損耗森瘪,但是對于較大的多個模型共用,建議API分離票堵,才能通過全局變量一次加載多次應(yīng)用的benefit

還有需要注意的地方:

就是模型加載的地方:
我們經(jīng)常會這樣寫:

def initialforlstm(self):
    if not os.path.exists(self.modelpath):
        self.trainlstmmodel()
        self.lstmmodel = load_model(self.modelpath)
    elif self.lstmmodel is None:
        try:
            keras.backend.clear_session()
        except Exception as e:
            logger.info(e)
        logger.info('Load model: {0}'.format(self.modelpath))
        self.lstmmodel = load_model(self.modelpath)

在Windows下啟動模型有關(guān)的API扼睬,非常順暢。
但是發(fā)布到LINUX的Dev服務(wù)器悴势,問題來了:
會出現(xiàn)這個問題窗宇,然后就崩了:
segmentation fault
經(jīng)過搜索萬能的谷歌,發(fā)現(xiàn)類似的問題特纤,但沒有解決方案:
Keras segmentation fault on load_model on Linux and not on Windows

不過因為有之前發(fā)布加載Keras模型API的經(jīng)驗军俊,嘗試將clear_session的順序倒置一下(clear_session是必須要的,否則無法在Flask加載任何Keras的模型)

    def initialforlstm(self):
        if not os.path.exists(self.modelpath):
            self.trainlstmmodel()
            self.lstmmodel = load_model(self.modelpath)
        elif self.lstmmodel is None:
            logger.info('Load model: {0}'.format(self.modelpath))
            try:
                self.lstmmodel = load_model(self.modelpath)
            except Exception as e:
                logger.info(e)
                keras.backend.clear_session()
                self.lstmmodel = load_model(self.modelpath)

問題解決捧存,it's done~~~

新的解決方案

如果保存模型的權(quán)重粪躬,然后運行時初始化加載權(quán)重文件,貌似可以解決這個問題昔穴,而且可以在process初始化時镰官,將多個model一次加載,多次應(yīng)用吗货。
具體代碼:

kerasUtil

def initialforlstm(self):
    modelfilename = os.path.splitext(os.path.basename(self.modelpath))
    modelyamlpath = os.path.join(os.path.dirname(self.modelpath),
                                 '{0}.yaml'.format(modelfilename[0]))
    modelweightpath = os.path.join(os.path.dirname(self.modelpath),
                                 '{0}_weight.h5'.format(modelfilename[0]))
    if not os.path.exists(self.modelpath):
        self.trainlstmmodel()
        self.loadmodel(modelyamlpath, modelweightpath)
    elif self.lstmmodel is None:
        try:
            self.loadmodel(modelyamlpath, modelweightpath)
        except Exception as e:
            logger.info(e)
            keras.backend.clear_session()
            self.loadmodel(modelyamlpath, modelweightpath)


def loadmodel(self, modelyamlpath, modelweightpath):
    if not os.path.exists(modelyamlpath) or not os.path.exists(modelweightpath):
        model = load_model(self.modelpath)
        yaml_string = model.to_yaml()
        open(modelyamlpath, 'w', encoding='utf-8').write(yaml_string)
        model.save_weights(modelweightpath)
        self.lstmmodel = model_from_yaml(open(modelyamlpath, encoding='utf-8').read())
        self.lstmmodel.load_weights(modelweightpath)
    else:
        logger.info('Load model: {0}, {1}'.format(modelyamlpath, modelweightpath))
        self.lstmmodel = model_from_yaml(open(modelyamlpath, encoding='utf-8').read())
        self.lstmmodel.load_weights(modelweightpath)

使用Keras模型的業(yè)務(wù)應(yīng)用部分代碼

kerasutil_A = KerasUtil(rawtextpath=r'A.csv',
                                  lstmmodelpath=r'A.h5',
                                  modeltype='bilstm')

kerasutil_B = KerasUtil(rawtextpath=r'B.csv',
                                  lstmmodelpath=r'B.h5',
                                  modeltype='bilstm')

kerasutil_C = KerasUtil(rawtextpath=r'C.csv',
                                  lstmmodelpath=r'C.h5',
                                  modeltype='bilstm')


def confirmwithmachinelearning(categoryid, phrasewithkeylist):
      for phrasewithkey in phrasewithkeylist:
          sentence = phrasewithkey['sentence']
          resultlist = []
          if categoryid == 'A':
              resultlist = kerasutil_A.predictcategory(sentence)
          if categoryid == 'B':
              resultlist = kerasutil_B.predictcategory(sentence)
          if categoryid == 'C':
              resultlist = kerasutil_C.predictcategory(sentence)
        ...
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泳唠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子宙搬,更是在濱河造成了極大的恐慌笨腥,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勇垛,死亡現(xiàn)場離奇詭異脖母,居然都是意外死亡,警方通過查閱死者的電腦和手機闲孤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門谆级,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人崭放,你說我怎么就攤上這事哨苛「胄祝” “怎么了币砂?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長玻侥。 經(jīng)常有香客問我决摧,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任掌桩,我火速辦了婚禮边锁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘波岛。我一直安慰自己茅坛,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布则拷。 她就那樣靜靜地躺著贡蓖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪煌茬。 梳的紋絲不亂的頭發(fā)上斥铺,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音坛善,去河邊找鬼晾蜘。 笑死,一個胖子當著我的面吹牛眠屎,可吹牛的內(nèi)容都是我干的剔交。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼组力,長吁一口氣:“原來是場噩夢啊……” “哼省容!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起燎字,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤腥椒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后候衍,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笼蛛,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年蛉鹿,在試婚紗的時候發(fā)現(xiàn)自己被綠了滨砍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡妖异,死狀恐怖惋戏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情他膳,我是刑警寧澤响逢,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站棕孙,受9級特大地震影響舔亭,放射性物質(zhì)發(fā)生泄漏些膨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一钦铺、第九天 我趴在偏房一處隱蔽的房頂上張望订雾。 院中可真熱鬧,春花似錦矛洞、人聲如沸洼哎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谱净。三九已至,卻和暖如春擅威,著一層夾襖步出監(jiān)牢的瞬間壕探,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工郊丛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留李请,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓厉熟,卻偏偏與公主長得像导盅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子揍瑟,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容