Keras - การเรียนรู้การถ่ายโอน - การเปลี่ยนรูปร่างเทนเซอร์


15

โพสต์นี้ดูเหมือนจะบ่งบอกว่าสิ่งที่ฉันต้องการทำสำเร็จนั้นเป็นไปไม่ได้ อย่างไรก็ตามฉันไม่มั่นใจในสิ่งนี้ - เนื่องจากสิ่งที่ฉันทำไปแล้วฉันไม่เห็นว่าทำไมสิ่งที่ฉันต้องการทำไม่สามารถทำได้ ...

ฉันมีชุดข้อมูลภาพสองชุดโดยที่หนึ่งมีรูปภาพของรูปร่าง (480, 720, 3) ในขณะที่อีกชุดมีรูปภาพของรูปร่าง (540, 960, 3)

ฉันเริ่มต้นโมเดลโดยใช้รหัสต่อไปนี้:

input = Input(shape=(480, 720, 3), name='image_input')

initial_model = VGG16(weights='imagenet', include_top=False)

for layer in initial_model.layers:
    layer.trainable = False

x = Flatten()(initial_model(input))
x = Dense(1000, activation='relu')(x)
x = BatchNormalization()(x)
x = Dropout(0.5)(x)
x = Dense(1000, activation='relu')(x)
x = BatchNormalization()(x)
x = Dropout(0.5)(x)
x = Dense(14, activation='linear')(x)

model = Model(inputs=input, outputs=x)
model.compile(loss='mse', optimizer='adam', metrics=['mae'])

ตอนนี้ฉันได้ฝึกฝนโมเดลนี้กับชุดข้อมูลเดิมฉันต้องการป๊อปเลเยอร์เทนเซอร์ปิดและเตรียมโมเดลด้วยเทนเซอร์อินพุทใหม่ที่มีรูปร่างที่ตรงกับขนาดภาพของชุดข้อมูลหลัง

model = load_model('path/to/my/trained/model.h5')
old_input = model.pop(0)
new_input = Input(shape=(540, 960, 3), name='image_input')
x = model(new_input)
m = Model(inputs=new_input, outputs=x)
m.save('transfer_model.h5')

ซึ่งทำให้เกิดข้อผิดพลาดนี้:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/aicg2/.local/lib/python2.7/site-packages/keras/engine/topology.py", line 2506, in save
    save_model(self, filepath, overwrite, include_optimizer)
  File "/home/aicg2/.local/lib/python2.7/site-packages/keras/models.py", line 106, in save_model
    'config': model.get_config()
  File "/home/aicg2/.local/lib/python2.7/site-packages/keras/engine/topology.py", line 2322, in get_config
    layer_config = layer.get_config()
  File "/home/aicg2/.local/lib/python2.7/site-packages/keras/engine/topology.py", line 2370, in get_config
    new_node_index = node_conversion_map[node_key]
KeyError: u'image_input_ib-0'

ในโพสต์ที่ฉันเชื่อมโยง maz ระบุว่ามีมิติที่ไม่ตรงกันซึ่งป้องกันการเปลี่ยนเลเยอร์ของโมเดล - ถ้าเป็นกรณีนี้แล้วฉันจะใส่เลเยอร์อินพุต (480, 720, 3) ไว้ข้างหน้าได้อย่างไร ของรุ่น VGG16 ซึ่งคาดว่าจะมีภาพ (224, 224, 3)?

ผมคิดว่าเป็นปัญหาที่มีแนวโน้มมากขึ้นว่าการส่งออกรูปแบบในอดีตของฉันเป็นที่คาดหวังว่าบางสิ่งบางอย่างที่แตกต่างจากสิ่งที่ฉันให้มันขึ้นอยู่กับสิ่ง fchollet ไม่ว่าจะเป็นในโพสต์นี้ ฉันสับสนทางวากยสัมพันธ์ แต่ฉันเชื่อว่าเซกเมนต์ทั้งหมดx = Layer()(x)กำลังสร้างเลเยอร์ทีละชิ้นจากอินพุต -> เอาท์พุทและเพียงแค่โยนอินพุตที่แตกต่างออกไปข้างหน้าก็ทำให้มันพัง

ฉันไม่รู้จริงๆว่า ...

ใครบางคนได้โปรดให้ความกระจ่างแก่ฉันเกี่ยวกับวิธีการทำสิ่งที่ฉันพยายามทำหรือถ้าเป็นไปไม่ได้อธิบายให้ฉันฟังหน่อย


คุณแก้ไขมันได้หรือไม่
tktktk0711

คำตอบ:


4

คุณสามารถทำได้โดยสร้างอินสแตนซ์โมเดล VGG16 ใหม่ด้วยรูปร่างอินพุตใหม่new_shapeและคัดลอกไปยังน้ำหนักชั้นทั้งหมด รหัสคือคร่าวๆ

new_model = VGG16(weights=None, input_shape=new_shape, include_top=False)
for new_layer, layer in zip(new_model.layers[1:], model.layers[1:]):
    new_layer.set_weights(layer.get_weights())

พยายามทำสิ่งนี้ด้วย
InceptionV3

@ r-zip ฉันได้รับข้อผิดพลาด: Traceback (most recent call last): File "predict_video11.py", line 67, in <module> new_layer.set_weights(layer.get_weights()) File "/usr/local/lib/python2.7/dist-packages/keras/engine/base_layer.py", line 1057, in set_weights 'provided weight shape ' + str(w.shape)) ValueError: Layer weight shape (3, 3, 33, 64) not compatible with provided weight shape (3, 3, 9, 64) และนั่นคือเลเยอร์อินพุตเพื่อใช้งาน[2:]หรือไม่
mLstudent33

1

ความกว้างและความสูงของเอาต์พุตของขนาดเอาต์พุตของ VGGnet เป็นส่วนที่คงที่ของความกว้างและความสูงของอินพุตเนื่องจากเลเยอร์เท่านั้นที่เปลี่ยนขนาดเหล่านั้นคือเลเยอร์รวม จำนวนแชนเนลในเอาต์พุตถูกกำหนดเป็นจำนวนฟิลเตอร์ในเลเยอร์ convolutional ล่าสุด เลเยอร์เรียบจะปรับรูปร่างนี้ใหม่เพื่อให้ได้มิติเดียวกับรูปร่าง:

((input_width * x) * (input_height * x) * channels)

โดยที่ x คือทศนิยมบางส่วน <1

จุดหลักคือรูปร่างของอินพุตไปยังเลเยอร์หนาแน่นนั้นขึ้นอยู่กับความกว้างและความสูงของอินพุตเข้ากับโมเดลทั้งหมด อินพุตรูปร่างไปยังเลเยอร์หนาแน่นนั้นไม่สามารถเปลี่ยนแปลงได้เนื่องจากจะหมายถึงการเพิ่มหรือลบโหนดออกจากเครือข่ายประสาท

วิธีหนึ่งในการหลีกเลี่ยงสิ่งนี้คือการใช้เลเยอร์ร่วมกันทั่วโลกแทนที่จะเป็นเลเยอร์เรียบ (โดยปกติแล้ว GlobalAveragePooling2D) สิ่งนี้จะค้นหาค่าเฉลี่ยต่อแชแนลที่ทำให้เกิดรูปร่างของอินพุตไปยังเลเยอร์หนาแน่น(channels,)ซึ่งไม่ขึ้นอยู่กับรูปร่างอินพุต แบบจำลองทั้งหมด

เมื่อทำสิ่งนี้เสร็จแล้วจะไม่มีเลเยอร์ในเครือข่ายขึ้นอยู่กับความกว้างและความสูงของอินพุทดังนั้นเลเยอร์อินพุตสามารถเปลี่ยนแปลงได้ด้วยสิ่งต่าง ๆ เช่น

input_layer = InputLayer(input_shape=(480, 720, 3), name="input_1")
model.layers[0] = input_layer

model.layers[0] = input_layerไม่ทำงานสำหรับฉันใน TF 2.1 ไม่มีข้อผิดพลาด แต่ไม่ได้แทนที่เลเยอร์จริง ดูเหมือนว่าการคัดลอกน้ำหนักอาจมีประสิทธิภาพมากกว่า (ดูคำตอบอื่น ๆ )
z0r

0

นี่เป็นอีกวิธีการหนึ่งที่ไม่เฉพาะรุ่น VGG

โปรดทราบว่าน้ำหนักของเลเยอร์หนาแน่นไม่สามารถคัดลอกได้ (และจะถูกเตรียมใช้งานใหม่) เรื่องนี้สมเหตุสมผลเพราะรูปร่างของตุ้มน้ำหนักแตกต่างกันในรุ่นเก่าและรุ่นใหม่

import keras
import numpy as np

def get_model():
    old_input_shape = (20, 20, 3)
    model = keras.models.Sequential()
    model.add(keras.layers.Conv2D(9, (3, 3), padding="same", input_shape=old_input_shape))
    model.add(keras.layers.MaxPooling2D((2, 2)))
    model.add(keras.layers.Flatten())
    model.add(keras.layers.Dense(1, activation="sigmoid"))
    model.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Adam(lr=0.0001), metrics=['acc'], )
    model.summary()
    return model

def change_model(model, new_input_shape=(None, 40, 40, 3)):
    # replace input shape of first layer
    model._layers[1].batch_input_shape = new_input_shape

    # feel free to modify additional parameters of other layers, for example...
    model._layers[2].pool_size = (8, 8)
    model._layers[2].strides = (8, 8)

    # rebuild model architecture by exporting and importing via json
    new_model = keras.models.model_from_json(model.to_json())
    new_model.summary()

    # copy weights from old model to new one
    for layer in new_model.layers:
        try:
            layer.set_weights(model.get_layer(name=layer.name).get_weights())
        except:
            print("Could not transfer weights for layer {}".format(layer.name))

    # test new model on a random input image
    X = np.random.rand(10, 40, 40, 3)
    y_pred = new_model.predict(X)
    print(y_pred)

    return new_model

if __name__ == '__main__':
    model = get_model()
    new_model = change_model(model)

0

kerassurgeonนี้ควรจะสวยง่ายด้วย ก่อนอื่นคุณต้องติดตั้งไลบรารี ขึ้นอยู่กับว่าคุณกำลังใช้ Keras ผ่าน TensorFlow (ด้วย tf 2.0 ขึ้นไป) หรือ Keras เป็นไลบรารี่แยกต่างหากมันจะต้องติดตั้งด้วยวิธีที่ต่างกัน

สำหรับ Keras ใน TF: pip install tfkerassurgeon( https://github.com/Raukk/tf-keras-surgeon ) สำหรับ Keras แบบสแตนด์อโลน: pip install kerassurgeon( https://github.com/BenWhetton/keras-surgeon )

ในการแทนที่อินพุต (ตัวอย่างด้วย TF 2.0; รหัสไม่ได้ทดสอบในปัจจุบัน):

from tensorflow import keras  # or import keras for standalone version
from tensorflow.keras.layers import Input

model = load_model('path/to/my/trained/model.h5')
new_input = Input(shape=(540, 960, 3), name='image_input')

# or kerassurgeon for standalone Keras
from tfkerassurgeon import delete_layer, insert_layer

model = delete_layer(model.layers[0])
# inserts before layer 0
model = insert_layer(model.layers[0], new_input)

0

@gebbissimo คำตอบสำหรับฉันใน TF2 ที่มีการดัดแปลงเพียงเล็กน้อยที่ฉันแบ่งปันด้านล่างในฟังก์ชั่นเดียว:

def change_input_size(model,h,w,ch=3):
   model._layers[0]._batch_input_shape = (None,h,w,ch)
   new_model = keras.models.model_from_json(model.to_json())
   new_model.summary()
   for layer,new_layer in zip(model.layers,new_model.layers):
      new_layer.set_weights(layer.get_weights())
   return new_model

0

นี่เป็นวิธีที่ฉันเปลี่ยนขนาดอินพุตในโมเดล Keras ฉันมีซีเอ็นเอ็นสองรุ่นหนึ่งอันที่มีขนาดอินพุต [ไม่มี, ไม่มี, 3] ในขณะที่อีกรุ่นหนึ่งมีขนาดอินพุต [512,512,3] ทั้งสองรุ่นมีน้ำหนักเท่ากัน โดยการใช้ set_weights (model.get_weights ()) น้ำหนักของรุ่น 1 สามารถโอนไปยังรุ่น 2 ได้

inputs = Input((None, None, 3))
.....
model = Model(inputs=[inputs], outputs=[outputs])
model.compile(optimizer='adam', loss='mean_squared_error')
model.load_weights('my_model_name.h5')

inputs2 = Input((512, 512, 3))
....
model2 = Model(inputs=[inputs2], outputs=[outputs])
model2.compile(optimizer='adam', loss='mean_squared_error')
model2.set_weights(model.get_weights())
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.