0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

遷移學(xué)習(xí)的原理,基于Keras實(shí)現(xiàn)遷移學(xué)習(xí)

zhKF_jqr_AI ? 來源:未知 ? 作者:李倩 ? 2018-05-09 03:44 ? 次閱讀

數(shù)據(jù)科學(xué)家Prakash Jay介紹了遷移學(xué)習(xí)的原理,基于Keras實(shí)現(xiàn)遷移學(xué)習(xí),以及遷移學(xué)習(xí)的常見情形。

Inception-V3

什么是遷移學(xué)習(xí)?

機(jī)器學(xué)習(xí)中的遷移學(xué)習(xí)問題,關(guān)注如何保存解決一個(gè)問題時(shí)獲得的知識(shí),并將其應(yīng)用于另一個(gè)相關(guān)的不同問題。

為什么遷移學(xué)習(xí)?

在實(shí)踐中,很少有人從頭訓(xùn)練一個(gè)卷積網(wǎng)絡(luò),因?yàn)楹茈y獲取足夠的數(shù)據(jù)集。使用預(yù)訓(xùn)練的網(wǎng)絡(luò)有助于解決大多數(shù)手頭的問題。

訓(xùn)練深度網(wǎng)絡(luò)代價(jià)高昂。即使使用數(shù)百臺(tái)配備了昂貴的GPU的機(jī)器,訓(xùn)練最復(fù)雜的模型也需要好多周。

決定深度學(xué)習(xí)的拓?fù)?特色/訓(xùn)練方法/超參數(shù)是沒有多少理論指導(dǎo)的黑魔法。

我的經(jīng)驗(yàn)

不要試圖成為英雄。

—— Andrej Karapathy

我面對(duì)的大多數(shù)計(jì)算機(jī)視覺問題沒有非常大的數(shù)據(jù)集(5000-40000圖像)。即使使用極端的數(shù)據(jù)增強(qiáng)策略,也很難達(dá)到像樣的精確度。而在少量數(shù)據(jù)集上訓(xùn)練數(shù)百萬參數(shù)的網(wǎng)絡(luò)通常會(huì)導(dǎo)致過擬合。所以遷移學(xué)習(xí)是我的救星。

遷移學(xué)習(xí)為何有效?

讓我們看下深度學(xué)習(xí)網(wǎng)絡(luò)學(xué)習(xí)了什么,靠前的層嘗試檢測(cè)邊緣,中間層嘗試檢測(cè)形狀,而靠后的層嘗試檢測(cè)高層數(shù)據(jù)特征。這些訓(xùn)練好的網(wǎng)絡(luò)通常有助于解決其他計(jì)算機(jī)視覺問題。

下面,讓我們看下如何使用Keras實(shí)現(xiàn)遷移學(xué)習(xí),以及遷移學(xué)習(xí)的常見情形。

基于Keras的簡(jiǎn)單實(shí)現(xiàn)

from keras import applications

from keras.preprocessing.image importImageDataGenerator

from keras import optimizers

from keras.models importSequential, Model

from keras.layers importDropout, Flatten, Dense, GlobalAveragePooling2D

from keras import backend as k

from keras.callbacks importModelCheckpoint, LearningRateScheduler, TensorBoard, EarlyStopping

img_width, img_height = 256, 256

train_data_dir = "data/train"

validation_data_dir = "data/val"

nb_train_samples = 4125

nb_validation_samples = 466

batch_size = 16

epochs = 50

model = applications.VGG19(weights = "imagenet", include_top=False, input_shape = (img_width, img_height, 3))

"""

層 (類型) 輸出形狀 參數(shù)數(shù)量

=================================================================

input_1 (InputLayer) (None, 256, 256, 3) 0

_________________________________________________________________

block1_conv1 (Conv2D) (None, 256, 256, 64) 1792

_________________________________________________________________

block1_conv2 (Conv2D) (None, 256, 256, 64) 36928

_________________________________________________________________

block1_pool (MaxPooling2D) (None, 128, 128, 64) 0

_________________________________________________________________

block2_conv1 (Conv2D) (None, 128, 128, 128) 73856

_________________________________________________________________

block2_conv2 (Conv2D) (None, 128, 128, 128) 147584

_________________________________________________________________

block2_pool (MaxPooling2D) (None, 64, 64, 128) 0

_________________________________________________________________

block3_conv1 (Conv2D) (None, 64, 64, 256) 295168

_________________________________________________________________

block3_conv2 (Conv2D) (None, 64, 64, 256) 590080

_________________________________________________________________

block3_conv3 (Conv2D) (None, 64, 64, 256) 590080

_________________________________________________________________

block3_conv4 (Conv2D) (None, 64, 64, 256) 590080

_________________________________________________________________

block3_pool (MaxPooling2D) (None, 32, 32, 256) 0

_________________________________________________________________

block4_conv1 (Conv2D) (None, 32, 32, 512) 1180160

_________________________________________________________________

block4_conv2 (Conv2D) (None, 32, 32, 512) 2359808

_________________________________________________________________

block4_conv3 (Conv2D) (None, 32, 32, 512) 2359808

_________________________________________________________________

block4_conv4 (Conv2D) (None, 32, 32, 512) 2359808

_________________________________________________________________

block4_pool (MaxPooling2D) (None, 16, 16, 512) 0

_________________________________________________________________

block5_conv1 (Conv2D) (None, 16, 16, 512) 2359808

_________________________________________________________________

block5_conv2 (Conv2D) (None, 16, 16, 512) 2359808

_________________________________________________________________

block5_conv3 (Conv2D) (None, 16, 16, 512) 2359808

_________________________________________________________________

block5_conv4 (Conv2D) (None, 16, 16, 512) 2359808

_________________________________________________________________

block5_pool (MaxPooling2D) (None, 8, 8, 512) 0

=================================================================

總參數(shù): 20,024,384.0

可訓(xùn)練參數(shù): 20,024,384.0

不可訓(xùn)練參數(shù): 0.0

"""

# 凍結(jié)不打算訓(xùn)練的層。這里我凍結(jié)了前5層。

for layer in model.layers[:5]:

layer.trainable = False

# 增加定制層

x = model.output

x = Flatten()(x)

x = Dense(1024, activation="relu")(x)

x = Dropout(0.5)(x)

x = Dense(1024, activation="relu")(x)

predictions = Dense(16, activation="softmax")(x)

# 創(chuàng)建最終模型

model_final = Model(input = model.input, output = predictions)

# 編譯最終模型

model_final.compile(loss = "categorical_crossentropy", optimizer = optimizers.SGD(lr=0.0001, momentum=0.9), metrics=["accuracy"])

# 數(shù)據(jù)增強(qiáng)

train_datagen = ImageDataGenerator(

rescale = 1./255,

horizontal_flip = True,

fill_mode = "nearest",

zoom_range = 0.3,

width_shift_range = 0.3,

height_shift_range=0.3,

rotation_range=30)

test_datagen = ImageDataGenerator(

rescale = 1./255,

horizontal_flip = True,

fill_mode = "nearest",

zoom_range = 0.3,

width_shift_range = 0.3,

height_shift_range=0.3,

rotation_range=30)

train_generator = train_datagen.flow_from_directory(

train_data_dir,

target_size = (img_height, img_width),

batch_size = batch_size,

class_mode = "categorical")

validation_generator = test_datagen.flow_from_directory(

validation_data_dir,

target_size = (img_height, img_width),

class_mode = "categorical")

# 保存模型

checkpoint = ModelCheckpoint("vgg16_1.h5", monitor='val_acc', verbose=1, save_best_only=True, save_weights_only=False, mode='auto', period=1)

early = EarlyStopping(monitor='val_acc', min_delta=0, patience=10, verbose=1, mode='auto')

# 訓(xùn)練模型

model_final.fit_generator(

train_generator,

samples_per_epoch = nb_train_samples,

epochs = epochs,

validation_data = validation_generator,

nb_val_samples = nb_validation_samples,

callbacks = [checkpoint, early])

遷移學(xué)習(xí)的常見情形

別忘了,靠前的層中的卷積特征更通用,靠后的層中的卷積特征更針對(duì)原本的數(shù)據(jù)集。遷移學(xué)習(xí)有4種主要場(chǎng)景:

1. 新數(shù)據(jù)集較小,和原數(shù)據(jù)集相似

如果我們嘗試訓(xùn)練整個(gè)網(wǎng)絡(luò),容易導(dǎo)致過擬合。由于新數(shù)據(jù)和原數(shù)據(jù)相似,因此我們期望卷積網(wǎng)絡(luò)中的高層特征和新數(shù)據(jù)集相關(guān)。因此,建議凍結(jié)所有卷積層,只訓(xùn)練分類器(比如,線性分類器):

for layer in model.layers:

layer.trainable = False

2. 新數(shù)據(jù)集較大,和原數(shù)據(jù)集相似

由于我們有更多數(shù)據(jù),我們更有自信,如果嘗試對(duì)整個(gè)網(wǎng)絡(luò)進(jìn)行精細(xì)調(diào)整,不會(huì)導(dǎo)致過擬合。

for layer in model.layers:

layer.trainable = True

其實(shí)默認(rèn)值就是True,上面的代碼明確指定所有層可訓(xùn)練,是為了更清楚地強(qiáng)調(diào)這一點(diǎn)。

由于開始的幾層檢測(cè)邊緣,你也可以選擇凍結(jié)這些層。比如,以下代碼凍結(jié)VGG19的前5層:

for layer in model.layers[:5]:

layer.trainable = False

3. 新數(shù)據(jù)集很小,但和原數(shù)據(jù)很不一樣

由于數(shù)據(jù)集很小,我們大概想要從靠前的層提取特征,然后在此之上訓(xùn)練一個(gè)分類器:(假定你對(duì)h5py有所了解)

from keras import applications

from keras.preprocessing.image importImageDataGenerator

from keras import optimizers

from keras.models importSequential, Model

from keras.layers importDropout, Flatten, Dense, GlobalAveragePooling2D

from keras import backend as k

from keras.callbacks importModelCheckpoint, LearningRateScheduler, TensorBoard, EarlyStopping

img_width, img_height = 256, 256

### 創(chuàng)建網(wǎng)絡(luò)

img_input = Input(shape=(256, 256, 3))

x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1')(img_input)

x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2')(x)

x = MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')(x)

# 塊2

x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1')(x)

x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2')(x)

x = MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool')(x)

model = Model(input = img_input, output = x)

model.summary()

"""

_________________________________________________________________

層 (類型) 輸出形狀 參數(shù)數(shù)量

=================================================================

input_1 (InputLayer) (None, 256, 256, 3) 0

_________________________________________________________________

block1_conv1 (Conv2D) (None, 256, 256, 64) 1792

_________________________________________________________________

block1_conv2 (Conv2D) (None, 256, 256, 64) 36928

_________________________________________________________________

block1_pool (MaxPooling2D) (None, 128, 128, 64) 0

_________________________________________________________________

block2_conv1 (Conv2D) (None, 128, 128, 128) 73856

_________________________________________________________________

block2_conv2 (Conv2D) (None, 128, 128, 128) 147584

_________________________________________________________________

block2_pool (MaxPooling2D) (None, 64, 64, 128) 0

=================================================================

總參數(shù):260,160.0

可訓(xùn)練參數(shù):260,160.0

不可訓(xùn)練參數(shù):0.0

"""

layer_dict = dict([(layer.name, layer) for layer in model.layers])

[layer.name for layer in model.layers]

"""

['input_1',

'block1_conv1',

'block1_conv2',

'block1_pool',

'block2_conv1',

'block2_conv2',

'block2_pool']

"""

import h5py

weights_path = 'vgg19_weights.h5'# ('https://github.com/fchollet/deep-learning-models/releases/download/v0.1/vgg19_weights_tf_dim_ordering_tf_kernels.h5)

f = h5py.File(weights_path)

list(f["model_weights"].keys())

"""

['block1_conv1',

'block1_conv2',

'block1_pool',

'block2_conv1',

'block2_conv2',

'block2_pool',

'block3_conv1',

'block3_conv2',

'block3_conv3',

'block3_conv4',

'block3_pool',

'block4_conv1',

'block4_conv2',

'block4_conv3',

'block4_conv4',

'block4_pool',

'block5_conv1',

'block5_conv2',

'block5_conv3',

'block5_conv4',

'block5_pool',

'dense_1',

'dense_2',

'dense_3',

'dropout_1',

'global_average_pooling2d_1',

'input_1']

"""

# 列出模型中的所有層的名稱

layer_names = [layer.name for layer in model.layers]

"""

# 提取`.h5`文件中每層的模型權(quán)重

>>> f["model_weights"]["block1_conv1"].attrs["weight_names"]

array([b'block1_conv1/kernel:0', b'block1_conv1/bias:0'],

dtype='|S21')

# 將這一數(shù)組分配給weight_names

>>> f["model_weights"]["block1_conv1"]["block1_conv1/kernel:0]

# 列表推導(dǎo)(weights)儲(chǔ)存層的權(quán)重和偏置

>>>layer_names.index("block1_conv1")

1

>>> model.layers[1].set_weights(weights)

# 為特定層設(shè)置權(quán)重。

使用for循環(huán)我們可以為整個(gè)網(wǎng)絡(luò)設(shè)置權(quán)重。

"""

for i in layer_dict.keys():

weight_names = f["model_weights"][i].attrs["weight_names"]

weights = [f["model_weights"][i][j] for j in weight_names]

index = layer_names.index(i)

model.layers[index].set_weights(weights)

import cv2

import numpy as np

import pandas as pd

from tqdm import tqdm

import itertools

import glob

features = []

for i in tqdm(files_location):

im = cv2.imread(i)

im = cv2.resize(cv2.cvtColor(im, cv2.COLOR_BGR2RGB), (256, 256)).astype(np.float32) / 255.0

im = np.expand_dims(im, axis =0)

outcome = model_final.predict(im)

features.append(outcome)

## 收集這些特征,創(chuàng)建一個(gè)dataframe,在其上訓(xùn)練一個(gè)分類器

以上代碼提取block2_pool特征。通常而言,由于這層有64 x 64 x 128特征,在其上訓(xùn)練一個(gè)分類器可能于事無補(bǔ)。我們可以加上一些全連接層,然后在其基礎(chǔ)上訓(xùn)練神經(jīng)網(wǎng)絡(luò)。

增加少量全連接層和一個(gè)輸出層。

為靠前的層設(shè)置權(quán)重,然后凍結(jié)。

訓(xùn)練網(wǎng)絡(luò)。

4. 新數(shù)據(jù)集很大,和原數(shù)據(jù)很不一樣

由于你有一個(gè)很大的數(shù)據(jù)集,你可以設(shè)計(jì)你自己的網(wǎng)絡(luò),或者使用現(xiàn)有的網(wǎng)絡(luò)。

你可以基于隨機(jī)初始化權(quán)重或預(yù)訓(xùn)練網(wǎng)絡(luò)權(quán)重初始化訓(xùn)練網(wǎng)絡(luò)。一般選擇后者。

你可以使用不同的網(wǎng)絡(luò),或者基于現(xiàn)有網(wǎng)絡(luò)做些改動(dòng)。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴

原文標(biāo)題:基于Keras進(jìn)行遷移學(xué)習(xí)

文章出處:【微信號(hào):jqr_AI,微信公眾號(hào):論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    什么是遷移學(xué)習(xí)?遷移學(xué)習(xí)實(shí)現(xiàn)方法與工具分析

    向于不要求海量數(shù)據(jù)也能達(dá)到應(yīng)用的精準(zhǔn)要求,因此“小數(shù)據(jù)學(xué)習(xí)”正在成為新的熱點(diǎn),以遷移學(xué)習(xí),強(qiáng)化學(xué)習(xí)為代表的小數(shù)據(jù)學(xué)習(xí)技術(shù),更能體現(xiàn)人工智能的
    發(fā)表于 05-11 09:12 ?1.2w次閱讀

    遷移學(xué)習(xí)訓(xùn)練網(wǎng)絡(luò)

    keras遷移學(xué)習(xí),改變VGG16輸出層,用imagenet權(quán)重retrain
    發(fā)表于 09-09 11:02

    【木棉花】學(xué)習(xí)筆記--分布式遷移

    ,遷移回原設(shè)備。創(chuàng)建工程同樣的,我們還是創(chuàng)建一個(gè)java的工程,然后將工程命名為 MyMigrate。具體是怎么實(shí)現(xiàn)的就可以參考我的前兩篇學(xué)習(xí)筆記:分布式數(shù)據(jù)庫、輕量級(jí)偏好數(shù)據(jù)庫學(xué)習(xí)
    發(fā)表于 09-05 10:49

    【木棉花】學(xué)習(xí)筆記--分布式遷移+回遷

    前言我又來啦,相信那些不了解分布式的童鞋,學(xué)習(xí)過我前幾篇學(xué)習(xí)筆記了之后是不是會(huì)更加理解了一些呢。當(dāng)然了,我這星期要整理到學(xué)習(xí)筆記中的也是和分布式能力相關(guān)的一個(gè)學(xué)習(xí)案例,那就是在上一期分
    發(fā)表于 09-07 20:09

    遷移學(xué)習(xí)

    神經(jīng)網(wǎng)絡(luò)訓(xùn)練方法卷積神經(jīng)網(wǎng)絡(luò)介紹經(jīng)典網(wǎng)絡(luò)結(jié)構(gòu)介紹章節(jié)目標(biāo):深入了解神經(jīng)網(wǎng)絡(luò)的組成、訓(xùn)練和實(shí)現(xiàn),掌握深度空間特征分布等關(guān)鍵概念,為深度遷移學(xué)習(xí)奠定知識(shí)基礎(chǔ) 三、遷移
    發(fā)表于 04-21 15:15

    基于局部分類精度的多源在線遷移學(xué)習(xí)算法

    近年來,遷移學(xué)習(xí)得到越來越多的關(guān)注,現(xiàn)有的在線遷移學(xué)習(xí)算法一般從單個(gè)源領(lǐng)域遷移知識(shí)。然而,當(dāng)源領(lǐng)域與目標(biāo)領(lǐng)域相似度較低時(shí),很難進(jìn)行有效的
    發(fā)表于 12-25 11:04 ?0次下載

    機(jī)器學(xué)習(xí)方法遷移學(xué)習(xí)的發(fā)展和研究資料說明

    近年來,遷移學(xué)習(xí)已經(jīng)引起了廣泛的關(guān)注和研究。遷移學(xué)習(xí)是運(yùn)用已存有的知識(shí)對(duì)不同但相關(guān)領(lǐng)域問題進(jìn)行求解的一種新的機(jī)器學(xué)習(xí)方法。它放寬了傳統(tǒng)機(jī)器
    發(fā)表于 07-17 08:00 ?0次下載
    機(jī)器<b class='flag-5'>學(xué)習(xí)</b>方法<b class='flag-5'>遷移</b><b class='flag-5'>學(xué)習(xí)</b>的發(fā)展和研究資料說明

    基于脈沖神經(jīng)網(wǎng)絡(luò)的遷移學(xué)習(xí)算法

    使用脈沖序列進(jìn)行數(shù)據(jù)處理的脈沖神經(jīng)網(wǎng)絡(luò)具有優(yōu)異的低功耗特性,但由于學(xué)習(xí)算法不成熟,多層網(wǎng)絡(luò)練存在收斂困難的問題。利用反向傳播網(wǎng)絡(luò)具有學(xué)習(xí)算法成熟和訓(xùn)練速度快的特點(diǎn),設(shè)計(jì)一種遷移學(xué)習(xí)算法
    發(fā)表于 05-24 16:03 ?15次下載

    基于遷移學(xué)習(xí)與圖像增強(qiáng)的夜間航拍車輛識(shí)別

    為了對(duì)夜間航拍圖片中的車輛進(jìn)行有效識(shí)別,提出基于二次遷移學(xué)習(xí)和 Retinex算法的圖像處理方法,僅利用小規(guī)模的數(shù)據(jù)集訓(xùn)練網(wǎng)絡(luò),采用基于 Faster r-CNN的深度學(xué)習(xí)算法即可實(shí)現(xiàn)
    發(fā)表于 06-21 14:59 ?16次下載

    基于WordNet模型的遷移學(xué)習(xí)文本特征對(duì)齊算法

    基于WordNet模型的遷移學(xué)習(xí)文本特征對(duì)齊算法
    發(fā)表于 06-27 16:14 ?8次下載

    基于遷移深度學(xué)習(xí)的雷達(dá)信號(hào)分選識(shí)別

    基于遷移深度學(xué)習(xí)的雷達(dá)信號(hào)分選識(shí)別 ? 來源:《軟件學(xué)報(bào)》?,作者王功明等 ? 摘要:? 針對(duì)當(dāng)前雷達(dá)信號(hào)分選識(shí)別算法普遍存在的低信噪比下識(shí)別能力差、特征參數(shù)提取困難、分類器模型參數(shù)復(fù)雜等問題,提出
    發(fā)表于 03-02 17:35 ?1520次閱讀

    一文詳解遷移學(xué)習(xí)

    遷移學(xué)習(xí)需要將預(yù)訓(xùn)練好的模型適應(yīng)新的下游任務(wù)。然而,作者觀察到,當(dāng)前的遷移學(xué)習(xí)方法通常無法關(guān)注與任務(wù)相關(guān)的特征。在這項(xiàng)工作中,作者探索了重新聚焦模型注意力以進(jìn)行
    的頭像 發(fā)表于 08-11 16:56 ?6329次閱讀
    一文詳解<b class='flag-5'>遷移</b><b class='flag-5'>學(xué)習(xí)</b>

    視覺深度學(xué)習(xí)遷移學(xué)習(xí)訓(xùn)練框架Torchvision介紹

    Torchvision是基于Pytorch的視覺深度學(xué)習(xí)遷移學(xué)習(xí)訓(xùn)練框架,當(dāng)前支持的圖像分類、對(duì)象檢測(cè)、實(shí)例分割、語義分割、姿態(tài)評(píng)估模型的遷移學(xué)習(xí)
    的頭像 發(fā)表于 09-22 09:49 ?913次閱讀
    視覺深度<b class='flag-5'>學(xué)習(xí)</b><b class='flag-5'>遷移</b><b class='flag-5'>學(xué)習(xí)</b>訓(xùn)練框架Torchvision介紹

    遷移學(xué)習(xí)的基本概念和實(shí)現(xiàn)方法

    遷移學(xué)習(xí)(Transfer Learning)是機(jī)器學(xué)習(xí)領(lǐng)域中的一個(gè)重要概念,其核心思想是利用在一個(gè)任務(wù)或領(lǐng)域中學(xué)到的知識(shí)來加速或改進(jìn)另一個(gè)相關(guān)任務(wù)或領(lǐng)域的學(xué)習(xí)過程。這種方法在數(shù)據(jù)稀缺
    的頭像 發(fā)表于 07-04 17:30 ?1678次閱讀

    預(yù)訓(xùn)練和遷移學(xué)習(xí)的區(qū)別和聯(lián)系

    預(yù)訓(xùn)練和遷移學(xué)習(xí)是深度學(xué)習(xí)和機(jī)器學(xué)習(xí)領(lǐng)域中的兩個(gè)重要概念,它們?cè)谔岣吣P托阅?、減少訓(xùn)練時(shí)間和降低對(duì)數(shù)據(jù)量的需求方面發(fā)揮著關(guān)鍵作用。本文將從定義、原理、應(yīng)用、區(qū)別和聯(lián)系等方面詳細(xì)探討預(yù)訓(xùn)
    的頭像 發(fā)表于 07-11 10:12 ?1066次閱讀