LoginSignup
12
14

More than 5 years have passed since last update.

django+keras+apache+mod_wsgiで<tensor> is not an element of this graphが出た話。

Last updated at Posted at 2017-11-15

django上にkerasを使ってディープラーニングを用いたアプリを作ろうとした時に

<tensor> is not an element of this graph

というエラーが出て困った時の解決策です。

状況

以下の画像は作ったアプリのUIです。
スクリーンショット 2017-11-15 17.18.08.png

フォームを設置して文章が送られてきたら、CNNを用いてネットビジネス度を判定するというアプリです。
このときにkerasのモデルを使って分類しています。
view.pyにはだいたいこんな事を書いています。

view.py
def get_text(request):

    result = classify(request.GET.get('text'))
    d = {
    'text': result
         }
    return render(request, 'index.html', d)

def classify(comments):

    raw_comment = comments
    predict_result = 0
    if comments != None:

         comment = [ord(x) for x in raw_comment if ord(x)<65000]

         comment = comment[:300]
         if len(comment) < 300:
                 comment += ([0] * (300 - len(comment)))
         ret = predict(np.array([comment]))
         predict_result = ret[0][0]

    return predict_result * 100

def predict(comments):
    ret = model.predict(comments)
    return ret

①get_textがフォームからgetメソッドで入力された文を取得してくる。
②classifyに入力された文を渡す。
③classifyが入力された文を数値化してそれをpredictに渡す
④predictが分類する
⑤classifyが確率を返す。
⑥get_textがhtmlページに表示させる
という流れです。

どこにもkerasのgraphを使う記述が無いのに
「is not an element of this graph」
とエラーを吐きます。
ややこしいことに、普通に出来ることもあれば時間をおいたらエラーを吐くこともあるという
最悪の状況でした。

解決策

view.py
def get_text(request):
    classify('run')#追加
    result = classify(request.GET.get('text'))
    d = {
    'text': result
         }
    return render(request, 'index.html', d)

って感じにしてやりました。
恐らくですが

・モデルを使って学習するときにpredictを行うプログラムが立ち上がらないまま分類してる。
・mod_wsgiとの連携がうまくいってない。
・初回にアクセスした時にも'text'が無いにもかかわらずclassifyしている
(ただ、ホームページは表示されてて「送信」ボタンを押したらエラーが出ていたのであまり関係ないかも)

今となっては原因不明ですがフォームから送られてきた文章をclassifyする前に、一度適当な文章を投げてやってpredictを行うプログラムを立ち上げてやれば問題なく動くようになりました。

[追記]

コメントを頂いてその方法を試してみるとすんなりいったのでこちらにも書いておきます。

view.py
import tensorflow as tf
graph = tf.get_default_graph()
def get_text(request):
    global graph
    with graph.as_default():
        result = classify(request.GET.get('text'))
        d = {
        'text': result
             }
        return render(request, 'index.html', d)

に変更してやると問題もなく動作しました。
コメントありがとうございました。

ちなみに、モデル作るときのソースコードは

keras.py

import sys, os.path, re, csv, os, glob
import pandas as pds
import numpy as np
import zipfile
import codecs
from keras.layers import Activation, Dense, Dropout, Flatten, Convolution2D, MaxPooling2D, Reshape, Input, merge, Conv2D
from keras.models import Model, Sequential
from keras.layers.embeddings import Embedding
from keras.layers.normalization import BatchNormalization
from keras.utils import np_utils
from keras.optimizers import Adam
from keras.callbacks import LearningRateScheduler, Callback, CSVLogger, ModelCheckpoint
import random



os.environ['KERAS_BACKEND'] = 'tensorflow'
#create model for CCNN

def create_model(embed_size=128, max_length=300, filter_sizes=(2, 3, 4, 5, 6, 7, 8), filter_num=64):
    inp = Input(shape=(max_length,))
    emb = Embedding(0xffff, embed_size)(inp)
    emb_ex = Reshape((max_length, embed_size, 1))(emb)
    convs = []
    # Conv2Dを複数通りかける
    for filter_size in filter_sizes:
        conv = Conv2D(filter_num, (filter_size, embed_size), activation="relu")(emb_ex)
        pool = MaxPooling2D(pool_size=(max_length - filter_size + 1, 1))(conv)
        convs.append(pool)
    convs_merged = merge(convs, mode='concat')
    reshape = Reshape((filter_num * len(filter_sizes),))(convs_merged)
    fc1 = Dense(64, activation="relu")(reshape)
    bn1 = BatchNormalization()(fc1)
    do1 = Dropout(0.5)(bn1)
    fc2 = Dense(1, activation='sigmoid')(do1)
    model = Model(input=inp, output=fc2)
    return model



def load_data(filepath, filepath1 ,max_length=300, min_length=10):
    """
    Twitterのプロフィール欄のコメントを入力データとする。

    eg
    京大3回/フルオートSNSマーケティング/Twitterを開く→
    規定の文章をツイート→月10万~50万の自動収入を見込めるシステムの無料テストユーザー募集中
    /テストユーザー故定員に達したら即打ち切ります/興味ある人はインスタより


    """

    comments = []
    comments2 = []

    #ビジネス系の人のプロフィールの読み込み
    with open(filepath) as f:
        for l in f:
            # 文字毎にUNICODEに変換しつつ顔文字を除外
            comment = [ord(x) for x in str(l).strip() if ord(x)<65000]
            # 長い部分は打ち切り
            comment = comment[:max_length]
            comment_len = len(comment)
            if comment_len < min_length:
                continue
            if comment_len < max_length:
                # 固定長にするために足りない部分を0で埋める
                comment += ([0] * (max_length - comment_len))
                comments.append((1, comment))
            else:
                 comments.append((1, comment))

    #普通の人のプロフィールの読み込み
    with open(filepath1) as f:
        for l1 in f2:
            # 文字毎にUNICODEに変換しつつ顔文字を除外
            comment2 = [ord(x) for x in str(l1).strip() if ord(x)<65000]


            # 長い部分は打ち切り
            comment2 = comment2[:max_length]
            comment2_len = len(comment2)
            if comment2_len < min_length:
                continue
            if comment2_len < max_length:
                # 固定長にするために足りない部分を0で埋める
                comment2 += ([0] * (max_length - comment2_len))
                comments2.append((0, comment2))
            else:
                comments2.append((0, comment2))



    comments.extend(comments2[:len(comments)])
    random.shuffle(comments)
    return comments




def train(inputs, targets, batch_size=100, epoch_count=100, max_length=300, model_filepath="model.h5", learning_rate=0.001):

    # 学習率を少しずつ下げるようにする
    start = learning_rate
    stop = learning_rate * 0.01
    learning_rates = np.linspace(start, stop, epoch_count)

    # モデル作成
    model = create_model(max_length=max_length)
    optimizer = Adam(lr=learning_rate)
    model.compile(loss='binary_crossentropy',
                  optimizer=optimizer,
                  metrics=['accuracy'])

    # 学習
    model.fit(inputs, targets,
              nb_epoch=epoch_count,
              batch_size=batch_size,
              verbose=1,
              validation_split=0.1,
              shuffle=True,
              callbacks=[
                  LearningRateScheduler(lambda epoch: learning_rates[epoch]),
              ])

    # モデルの保存
    model.save(model_filepath)



if __name__ == "__main__":
    comments = load_data(filepath='/data',filepath1='/data1')
    print(comments)
    input_values = []
    category_values = []
    for category_value, input_value in comments:
        input_values.append(input_value)
        category_values.append(category_value)
    input_values = np.array(input_values)
    category_values = np.array(category_values)
    train(input_values, category_values)

こんな感じで、ローカルでテストするなら

classyfy.py
import numpy as np
from keras.models import load_model

def predict(comments, model_filepath="model.h5"):
    model = load_model(model_filepath)
    ret = model.predict(comments)
    return ret

if __name__ == "__main__":
    raw_comment = "22歳/トレード/投資/暗号通貨/月収7桁/ スマホ可/ 勝率90%越え/SPADE♠︎代表/生徒平均月収25,5万無料攻略マニュアル【30名限定で】配布中"
    comment = [ord(x) for x in raw_comment.strip()]

    comment = comment[:300]
    if len(comment) < 10:
        exit("too short!!")
    if len(comment) < 300:
        comment += ([0] * (300 - len(comment)))
    ret = predict(np.array([comment]))
    predict_result = ret[0][0]
    print('input data :'+raw_comment+'\n')
    print ("ネットビジネス度: {}%".format(predict_result * 100))

って感じです。

12
14
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
14