KerasTensor' object has no attribute 'graph' for keras2onnx

I am using Ubuntu 18 and Tensorflow 2.5, ONNX 1.7, and Keras 2.4. I’m trying to convert Keras code to Onnx. I have the following Python code:

from __future__ import print_function
import warnings
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from keras import backend as K
from keras.callbacks import CSVLogger
from keras.layers import *
from keras.models import Model
from keras.optimizers import Adam
from scipy.io import loadmat
from scipy.misc import imread
from skimage.io import imsave
from skimage.transform import resize
from scipy.io import loadmat
import cv2

Ny=512	
target_size = (Ny, Ny)
warnings.filterwarnings('ignore')
%matplotlib inline
LR= 1e-5
E, BS = 200,4 

def get_unet(img_rows, img_cols):
    inputs = Input((img_rows, img_cols, 1))
    conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(inputs)
    conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv1)
    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)
    conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool1)
    conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv2)
    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)
    conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool2)
    conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv3)
    pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)
    conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool3)
    conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv4)
    drop4 = Dropout(0.5)(conv4)
    pool4 = MaxPooling2D(pool_size=(2, 2))(drop4)
    conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool4)
    conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv5)
    drop5 = Dropout(0.5)(conv5)
    up6 = Conv2D(512, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(drop5))
    merge6 = concatenate([drop4,up6], axis = 3)
    conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge6)
    conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv6)
    up7 = Conv2D(256, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv6))
    merge7 = concatenate([conv3,up7], axis = 3)
    conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge7)
    conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv7)
    up8 = Conv2D(128, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv7))
    merge8 = concatenate([conv2,up8], axis = 3)
    conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge8)
    conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv8)
    up9 = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv8))
    merge9 = concatenate([conv1,up9], axis = 3)
    conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge9)
    conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)
    conv9 = Conv2D(2, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)
    conv10 = Conv2D(1, 1, activation = 'sigmoid')(conv9)

    model = Model(inputs=[inputs], outputs=[conv10])
    model.compile(optimizer=Adam(lr=LR), 
                      loss='binary_crossentropy', metrics=['binary_crossentropy'])
    return model

outName='Unet'
trainDir='train' 
trainAnnotDir='trainannot'


#training images
import os
Image_directory = './' + trainDir + '/'
Label_directory = './' + trainAnnotDir + '/'

image_lists = {os.path.basename(x[0]): x[2] for x in os.walk('./'+trainDir)}
n = len(image_lists[trainDir])
ids = os.listdir(trainDir)

train_images=np.zeros([n, Ny,Ny,1], dtype=float)
annot_train=np.zeros([n, Ny,Ny,1], dtype=float)
for j in range(0, n):
    l=ids[j][:-4]
    img = cv2.imread(Image_directory+l+'.bmp', 0)/255.0 
    label = cv2.imread(Label_directory+l+'.bmp', 0)/255.0
    train_images[j,:,:,0], annot_train[j,:,:,0] =img, label


#test images
testDir='test'
testAnnotDir='testannot'
Image_directory = './' + testDir + '/'
Label_directory = './' + testAnnotDir + '/'

image_lists = {os.path.basename(x[0]): x[2] for x in os.walk('./'+testDir)}
n = len(image_lists[testDir])#16
ids = os.listdir(testDir)

nTestTotal=21
test_images=np.zeros([nTestTotal, Ny,Ny,1], dtype=float)
annot_test=np.zeros([nTestTotal, Ny,Ny,1], dtype=float)
nTest=0
for j in range(0, n): #n):
    l=ids[j][:-4]
    img = cv2.imread(Image_directory+l+'.bmp',0)/255.0  
    label = cv2.imread(Label_directory+l+'.bmp',0)/255.0
    if img.shape[0]==Ny:
        test_images[nTest,:,:,0], annot_test[nTest,:,:,0] =img, label
        nTest+=1

C = np.concatenate([annot_test, annot_train])
I = np.concatenate([test_images, train_images])

#mixed precision
import tensorflow as tf
from tensorflow.keras.mixed_precision import experimental as mixed_precision
policy = mixed_precision.Policy('mixed_float16')
mixed_precision.set_policy(policy)
config = tf.compat.v1.ConfigProto()
config.gpu_options.allow_growth = True
session = tf.compat.v1.Session(config=config)


csv_callback = CSVLogger('history.log', append=True)
unet = get_unet(Ny, Ny)

filepath="W-E_{epoch:02d}-L_{loss:.6f}"+outName+".h5"
callbacks = keras.callbacks.ModelCheckpoint('./'+filepath, monitor='loss', verbose=1,
    save_best_only=True, mode='min', period=1)

history = unet.fit(I, C, verbose=2, epochs=E, batch_size=BS, validation_split=0.1, callbacks=[csv_callback])

from tensorflow.keras.models import save_model
save_model(unet, outName+'.keras')

I then tried to convert it to ONNX with this:

from keras.models import load_model
import onnx
import keras2onnx
import tensorflow as tf

outName='Unet'
E,BS,LR=200,4,1e-5
Estr, LRstr='200', '0.00001'

onnx_model_name = 'unetMaster.onnx'

model = load_model(outName+'.keras')
onnx_model = keras2onnx.convert_keras(model, model.name)
onnx.save_model(onnx_model, onnx_model_name)

but it gave the error

File "kerasToOnnx.py", line 15, in <module>
    onnx_model = keras2onnx.convert_keras(model, model.name)
  File "/home/me/.local/lib/python3.8/site-packages/keras2onnx/main.py", line 62, in convert_keras
    tf_graph = build_layer_output_from_model(model, output_dict, input_names, output_names)
  File "/home/me/.local/lib/python3.8/site-packages/keras2onnx/_parser_tf.py", line 304, in build_layer_output_from_model
    graph = model.outputs[0].graph
AttributeError: 'KerasTensor' object has no attribute 'graph'

maybe I’m getting this error because ONNX Only works with Tensorflow 1 instead of 2? can anyone help with this?

Hi @mke489,
Suggest you to raise the query in ONNX forum.

Thanks!