LoginSignup
fukudashunya
@fukudashunya

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

AttributeError: 'SymbolicTensor' object has no attribute 'numpy'を解決したい

AttributeError: 'SymbolicTensor' object has no attribute 'numpy'を解決したい

Pythonでkerasを使ったMLPモデルで予測をしています。
評価関数はF1マクロ平均でカスタム関数を作っています。
使用環境はGoogle Colabです。
目的変数は保険料の価格帯で、[0,1,2]から構成される多クラス分類です。
data = pd.read_csv('MYPATH')でローカルからデータを読み込んでいます。
データの構造は、訓練データとテストデータが一緒になっているため(テストデータの目的変巣である"charges"は-1が格納されています)、分割しています。
その後、訓練データを更に学習用とバリデーション用に分割し、historyで評価しています。
また、目的変数は、0が非常に多く、1と2が少ない不均衡データとなっているので、class_weights = {0: 1.0, 1: 5.0, 2: 5.0}でリサンプリングしています。(ここは今回のエラーとは関係ないと思います。)

発生している問題・エラー

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-9-61406dba8cad> in <cell line: 70>()
     68 
     69 # モデルのトレーニング
---> 70 history = model.fit(X_train_split, y_train_split, validation_data=(X_val, y_val), epochs=50, batch_size=32, verbose=1)
     71 
     72 # バリデーションデータで予測

2 frames
/tmp/__autograph_generated_filevx5nsktk.py in tf__update_state(self, y_true, y_pred, sample_weight)
     10                 y_true_labels = ag__.converted_call(ag__.ld(tf).argmax, (ag__.ld(y_true),), dict(axis=1), fscope)
     11                 y_pred_labels = ag__.converted_call(ag__.ld(tf).argmax, (ag__.ld(y_pred),), dict(axis=1), fscope)
---> 12                 y_true_np = ag__.converted_call(ag__.ld(y_true).numpy, (), None, fscope)
     13                 y_pred_np = ag__.converted_call(ag__.ld(y_pred).numpy, (), None, fscope)
     14                 f1 = ag__.converted_call(ag__.ld(tf).py_function, (), dict(func=ag__.ld(custom_f1_score), inp=[ag__.ld(y_true_np), ag__.ld(y_pred_np)], Tout=ag__.ld(tf).float32, name='f1_score'), fscope)

AttributeError: 'SymbolicTensor' object has no attribute 'numpy'

該当するソースコード

# Google Driveをマウント
from google.colab import drive
drive.mount('/content/drive')

# 必要なライブラリのインストール
!pip install tensorflow

import pandas as pd
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix, f1_score

# カスタムF1スコアメトリックの定義
class F1ScoreMacro(tf.keras.metrics.Metric):
    def __init__(self, name='f1_score_macro', **kwargs):
        super(F1ScoreMacro, self).__init__(name=name, **kwargs)
        self.f1 = self.add_weight(name='f1', initializer='zeros', dtype=tf.float32)

    def update_state(self, y_true, y_pred, sample_weight=None):
        y_true = tf.argmax(y_true, axis=1, output_type=tf.int32)
        y_pred = tf.argmax(y_pred, axis=1, output_type=tf.int32)

        # Extract class labels from one-hot encoded tensors
        y_true_labels = tf.argmax(y_true, axis=1)
        y_pred_labels = tf.argmax(y_pred, axis=1)

        y_true_np = y_true.numpy()
        y_pred_np = y_pred.numpy()

        f1 = tf.py_function(func=custom_f1_score, inp=[y_true_np, y_pred_np], Tout=tf.float32, name='f1_score')
        self.f1.assign_add(f1)

    def result(self):
        return self.f1

    def reset_states(self):
        self.f1.assign(0.0)

# データの読み込み
data = pd.read_csv('MYPATH')

# 本来のテストデータを分離
train_data = data[data['charges'] != -1]
test_data = data[data['charges'] == -1]

# トレーニングデータの特徴量と目的変数に分割
X_train = train_data.drop('charges', axis=1)
y_train = pd.get_dummies(train_data['charges'])  # One-hotエンコーディング

# テストデータの特徴量に分割(目的変数は使用しない)
X_test = test_data.drop('charges', axis=1)

# 学習用データをさらにトレーニングセットとバリデーションセットに分割
X_train_split, X_val, y_train_split, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=42)

# クラス重みの定義
class_weights = {0: 1.0, 1: 5.0, 2: 5.0}

# モデルの構築
model = Sequential()
model.add(Dense(64, input_dim=X_train_split.shape[1], activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(32, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(3, activation='softmax'))  # 出力層、クラス数は3

# モデルのコンパイル
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=[F1ScoreMacro()])

# モデルのトレーニング
history = model.fit(X_train_split, y_train_split, validation_data=(X_val, y_val), epochs=50, batch_size=32, verbose=1)

自分で試したこと

カスタム関数である、F1ScoreMacro()の内容を、historyへ渡すときにエラーが起きてる認識です。
具体的には、カスタム関数内のTensor型のデータをNumpy型へ変換できずに、矛盾が起きていると考えています。
kerasはgraph executionで動くので、eager excutionに直すための"run_eagerly=True"を入れたり、
F1ScoreMacro()にNumpyに変換するためのコードを入れたりしましたが、改善されないので、質問しました。
(以下、そのコードです)

class F1ScoreMacro(tf.keras.metrics.Metric):
    def __init__(self, name='f1_score_macro', **kwargs):
        super(F1ScoreMacro, self).__init__(name=name, **kwargs)
        self.f1 = self.add_weight(name='f1', initializer='zeros', dtype=tf.float32)

    def update_state(self, y_true, y_pred, sample_weight=None):
        y_true = tf.argmax(y_true, axis=1)
        y_pred = tf.argmax(y_pred, axis=1)
        
        def f1_score_np(y_true, y_pred):
            return f1_score(y_true, y_pred, average='macro')
        
        f1 = tf.numpy_function(f1_score_np, [y_true, y_pred], tf.float32)
        self.f1.assign_add(f1)

    def result(self):
        return self.f1

    def reset_states(self):
        self.f1.assign(0.0)

かなり雑多な質問になってしまいましたが、どなたか改善方法などわかる方いましたら、ぜひご教授いただきたいです。
よろしくお願いいたします。

0

2Answer

Comments

  1. @fukudashunya

    Questioner

    回答ありがとうございます。
    必要なライブラリはすべてimportしています。
    記載しておらず、見にくくなってしまって申し訳ありません。
    引き続き、わかることがございましたら、回答お願いいたします。

  2. # モデルの構築から2行目のX_train_splitが未定義となってしまいました。
    コードをすべて貼るのは難しいのでしょうか?

  3. @fukudashunya

    Questioner

    ありがとうございます。コード全文と簡単な説明を付け加えましたので、ご確認ください。
    読み込んでいるファイルがこちらの環境に依存しているので、そのままでは動かないと思います。(※大変申し訳ございませんが、データの直接の提供に関しては、競技コンペの性質上、公開できないことをご承知おきください)
    何かわかることがありましたら、引き続きよろしくお願いいたします。

  4. 横から失礼します。

    (※大変申し訳ございませんが、データの直接の提供に関しては、競技コンペの性質上、公開できないことをご承知おきください)

    どちらのコンペでしょうか?質問にURLを貼っていいただけると回答者の参考になるかと思います。

自己解決

皆さん、ご回答いただきありがとうございました。
以下の修正をすることで、エラーの解消になりましたので、ご報告いたします。

①Eager Executionを明示的に有効にする
tf.config.experimental_run_functions_eagerly(True)
②tf.numpy_functionのdtypeをtf.float64に設定する
tf.numpy_function(f1_score_np, [y_true, y_pred], tf.float64)
③self.f1.assign_addの引数をtf.castでfloat32にキャストする
self.f1.assign_add(tf.cast(f1, tf.float32))

以上の結果、MLPモデルを実装することができました。
コード全文も記載します。
皆さん回答いただきまして、ありがとうございました。

# Google Driveをマウント
from google.colab import drive
drive.mount('/content/drive')

# 必要なライブラリのインストール
!pip install tensorflow

import pandas as pd
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix, f1_score

# Eager Executionを有効にする(TensorFlow 2.xではデフォルトで有効)
tf.config.experimental_run_functions_eagerly(True)

# tf.data APIでもEager Executionを有効にする
tf.data.experimental.enable_debug_mode()

# カスタムF1スコアメトリックの定義
class F1ScoreMacro(tf.keras.metrics.Metric):
    def __init__(self, name='f1_score_macro', **kwargs):
        super(F1ScoreMacro, self).__init__(name=name, **kwargs)
        self.f1 = self.add_weight(name='f1', initializer='zeros', dtype=tf.float32)

    def update_state(self, y_true, y_pred, sample_weight=None):
        y_true = tf.argmax(y_true, axis=1)
        y_pred = tf.argmax(y_pred, axis=1)

        def f1_score_np(y_true, y_pred):
            return f1_score(y_true, y_pred, average='macro')

        f1 = tf.numpy_function(f1_score_np, [y_true, y_pred], tf.float64)
        self.f1.assign_add(tf.cast(f1, tf.float32))

    def result(self):
        return self.f1

    def reset_state(self):  # メソッド名を変更
        self.f1.assign(0.0)

# データの読み込み
data = pd.read_csv('MYPATH')

# 本来のテストデータを分離
train_data = data[data['charges'] != -1]
test_data = data[data['charges'] == -1]

# トレーニングデータの特徴量と目的変数に分割
X_train = train_data.drop('charges', axis=1)
y_train = pd.get_dummies(train_data['charges'])  # One-hotエンコーディング

# テストデータの特徴量に分割(目的変数は使用しない)
X_test = test_data.drop('charges', axis=1)

# 学習用データをさらにトレーニングセットとバリデーションセットに分割
X_train_split, X_val, y_train_split, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=42)

# クラス重みの定義
class_weights = {0: 1.0, 1: 5.0, 2: 5.0}

# モデルの構築
model = Sequential()
model.add(Dense(64, input_dim=X_train_split.shape[1], activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(32, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(3, activation='softmax'))  # 出力層、クラス数は3

# モデルのコンパイル
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=[F1ScoreMacro()])

# モデルのトレーニング
history = model.fit(X_train_split, y_train_split, validation_data=(X_val, y_val), epochs=50, batch_size=32, verbose=1, class_weight=class_weights)

# バリデーションデータで予測
y_pred_prob = model.predict(X_val)
y_pred = np.argmax(y_pred_prob, axis=1)
y_val_labels = np.argmax(y_val.values, axis=1)

# モデルの評価
conf_matrix = confusion_matrix(y_val_labels, y_pred)
class_report = classification_report(y_val_labels, y_pred, zero_division=0)
f1 = f1_score(y_val_labels, y_pred, average='macro')

print("Confusion Matrix:\n", conf_matrix)
print("\nClassification Report:\n", class_report)
print("\nMacro Average F1 Score:", f1)
0

Comments

  1. 解決したのあれば、本問をクローズしましょう。

Your answer might help someone💌