亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺談keras通過(guò)model.fit_generator訓(xùn)練模型(節(jié)省內(nèi)存)

 更新時(shí)間:2020年06月17日 14:40:27   作者:Donreen  
這篇文章主要介紹了淺談keras通過(guò)model.fit_generator訓(xùn)練模型(節(jié)省內(nèi)存),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧

前言

前段時(shí)間在訓(xùn)練模型的時(shí)候,發(fā)現(xiàn)當(dāng)訓(xùn)練集的數(shù)量過(guò)大,并且輸入的圖片維度過(guò)大時(shí),很容易就超內(nèi)存了,舉個(gè)簡(jiǎn)單例子,如果我們有20000個(gè)樣本,輸入圖片的維度是224x224x3,用float32存儲(chǔ),那么如果我們一次性將全部數(shù)據(jù)載入內(nèi)存的話,總共就需要20000x224x224x3x32bit/8=11.2GB 這么大的內(nèi)存,所以如果一次性要加載全部數(shù)據(jù)集的話是需要很大內(nèi)存的。

如果我們直接用keras的fit函數(shù)來(lái)訓(xùn)練模型的話,是需要傳入全部訓(xùn)練數(shù)據(jù),但是好在提供了fit_generator,可以分批次的讀取數(shù)據(jù),節(jié)省了我們的內(nèi)存,我們唯一要做的就是實(shí)現(xiàn)一個(gè)生成器(generator)。

1.fit_generator函數(shù)簡(jiǎn)介

fit_generator(generator, 
steps_per_epoch=None, 
epochs=1, 
verbose=1, 
callbacks=None, 
validation_data=None, 
validation_steps=None, 
class_weight=None, 
max_queue_size=10, 
workers=1, 
use_multiprocessing=False, 
shuffle=True, 
initial_epoch=0)

參數(shù):

generator:一個(gè)生成器,或者一個(gè) Sequence (keras.utils.Sequence) 對(duì)象的實(shí)例。這是我們實(shí)現(xiàn)的重點(diǎn),后面會(huì)著介紹生成器和sequence的兩種實(shí)現(xiàn)方式。

steps_per_epoch:這個(gè)是我們?cè)诿總€(gè)epoch中需要執(zhí)行多少次生成器來(lái)生產(chǎn)數(shù)據(jù),fit_generator函數(shù)沒有batch_size這個(gè)參數(shù),是通過(guò)steps_per_epoch來(lái)實(shí)現(xiàn)的,每次生產(chǎn)的數(shù)據(jù)就是一個(gè)batch,因此steps_per_epoch的值我們通過(guò)會(huì)設(shè)為(樣本數(shù)/batch_size)。如果我們的generator是sequence類型,那么這個(gè)參數(shù)是可選的,默認(rèn)使用len(generator) 。

epochs:即我們訓(xùn)練的迭代次數(shù)。

verbose:0, 1 或 2。日志顯示模式。 0 = 安靜模式, 1 = 進(jìn)度條, 2 = 每輪一行

callbacks:在訓(xùn)練時(shí)調(diào)用的一系列回調(diào)函數(shù)。

validation_data:和我們的generator類似,只是這個(gè)使用于驗(yàn)證的,不參與訓(xùn)練。

validation_steps:和前面的steps_per_epoch類似。

class_weight:可選的將類索引(整數(shù))映射到權(quán)重(浮點(diǎn))值的字典,用于加權(quán)損失函數(shù)(僅在訓(xùn)練期間)。 這可以用來(lái)告訴模型「更多地關(guān)注」來(lái)自代表性不足的類的樣本。(感覺這個(gè)參數(shù)用的比較少)

max_queue_size:整數(shù)。生成器隊(duì)列的最大尺寸。默認(rèn)為10.

workers:整數(shù)。使用的最大進(jìn)程數(shù)量,如果使用基于進(jìn)程的多線程。 如未指定,workers 將默認(rèn)為 1。如果為 0,將在主線程上執(zhí)行生成器。

use_multiprocessing:布爾值。如果 True,則使用基于進(jìn)程的多線程。默認(rèn)為False。

shuffle:是否在每輪迭代之前打亂 batch 的順序。 只能與Sequence(keras.utils.Sequence) 實(shí)例同用。

initial_epoch: 開始訓(xùn)練的輪次(有助于恢復(fù)之前的訓(xùn)練)

2.generator實(shí)現(xiàn)

2.1生成器的實(shí)現(xiàn)方式

樣例代碼:

import keras
from keras.models import Sequential
from keras.layers import Dense
import numpy as np
from sklearn.model_selection import train_test_split
from PIL import Image

def process_x(path):
 img = Image.open(path)
 img = img.resize((96,96))
 img = img.convert('RGB')
 img = np.array(img)

 img = np.asarray(img, np.float32) / 255.0
 #也可以進(jìn)行進(jìn)行一些數(shù)據(jù)數(shù)據(jù)增強(qiáng)的處理
 return img

count =1
def generate_arrays_from_file(x_y):
 #x_y 是我們的訓(xùn)練集包括標(biāo)簽,每一行的第一個(gè)是我們的圖片路徑,后面的是我們的獨(dú)熱化后的標(biāo)簽

 global count
 batch_size = 8
 while 1:
  batch_x = x_y[(count - 1) * batch_size:count * batch_size, 0]
  batch_y = x_y[(count - 1) * batch_size:count * batch_size, 1:]

  batch_x = np.array([process_x(img_path) for img_path in batch_x])
  batch_y = np.array(batch_y).astype(np.float32)
  print("count:"+str(count))
  count = count+1
  yield (batch_x, batch_y)

model = Sequential()
model.add(Dense(units=1000, activation='relu', input_dim=2))
model.add(Dense(units=2, activation='softmax'))
model.compile(loss='categorical_crossentropy',optimizer='sgd',metrics=['accuracy'])

x_y = []
model.fit_generator(generate_arrays_from_file(x_y),steps_per_epoch=10, epochs=2,max_queue_size=1,workers=1)

在理解上面代碼之前我們需要首先了解yield的用法。

yield關(guān)鍵字:

我們先通過(guò)一個(gè)例子看一下yield的用法:

def foo():
 print("starting...")
 while True:
  res = yield 4
  print("res:",res)
g = foo()
print(next(g))
print("----------")
print(next(g))

運(yùn)行結(jié)果:

starting...
4
----------
res: None
4

帶yield的函數(shù)是一個(gè)生成器,而不是一個(gè)函數(shù)。因?yàn)閒oo函數(shù)中有yield關(guān)鍵字,所以foo函數(shù)并不會(huì)真的執(zhí)行,而是先得到一個(gè)生成器的實(shí)例,當(dāng)我們第一次調(diào)用next函數(shù)的時(shí)候,foo函數(shù)才開始行,首先先執(zhí)行foo函數(shù)中的print方法,然后進(jìn)入while循環(huán),循環(huán)執(zhí)行到y(tǒng)ield時(shí),yield其實(shí)相當(dāng)于return,函數(shù)返回4,程序停止。所以我們第一次調(diào)用next(g)的輸出結(jié)果是前面兩行。

然后當(dāng)我們?cè)俅握{(diào)用next(g)時(shí),這個(gè)時(shí)候是從上一次停止的地方繼續(xù)執(zhí)行,也就是要執(zhí)行res的賦值操作,因?yàn)?已經(jīng)在上一次執(zhí)行被return了,隨意賦值res為None,然后執(zhí)行print(“res:”,res)打印res: None,再次循環(huán)到y(tǒng)ield返回4,程序停止。

所以yield關(guān)鍵字的作用就是我們能夠從上一次程序停止的地方繼續(xù)執(zhí)行,這樣我們用作生成器的時(shí)候,就避免一次性讀入數(shù)據(jù)造成內(nèi)存不足的情況。

現(xiàn)在看到上面的示例代碼:

generate_arrays_from_file函數(shù)就是我們的生成器,每次循環(huán)讀取一個(gè)batch大小的數(shù)據(jù),然后處理數(shù)據(jù),并返回。x_y是我們的把路徑和標(biāo)簽合并后的訓(xùn)練集,類似于如下形式:

['data/img\\fimg_4092.jpg' '0' '1' '0' '0' '0' ]

至于格式不一定要這樣,可以是自己的格式,至于怎么處理,根于自己的格式,在process_x進(jìn)行處理,這里因?yàn)槭谴娣诺膱D片路徑,所以在process_x函數(shù)的主要作用就是讀取圖片并進(jìn)行歸一化等操作,也可以在這里定義自己需要進(jìn)行的操作,例如對(duì)圖像進(jìn)行實(shí)時(shí)數(shù)據(jù)增強(qiáng)。

2.2使用Sequence實(shí)現(xiàn)generator

示例代碼:

class BaseSequence(Sequence):
 """
 基礎(chǔ)的數(shù)據(jù)流生成器,每次迭代返回一個(gè)batch
 BaseSequence可直接用于fit_generator的generator參數(shù)
 fit_generator會(huì)將BaseSequence再次封裝為一個(gè)多進(jìn)程的數(shù)據(jù)流生成器
 而且能保證在多進(jìn)程下的一個(gè)epoch中不會(huì)重復(fù)取相同的樣本
 """
 def __init__(self, img_paths, labels, batch_size, img_size):
  #np.hstack在水平方向上平鋪
  self.x_y = np.hstack((np.array(img_paths).reshape(len(img_paths), 1), np.array(labels)))
  self.batch_size = batch_size
  self.img_size = img_size

 def __len__(self):
  #math.ceil表示向上取整
  #調(diào)用len(BaseSequence)時(shí)返回,返回的是每個(gè)epoch我們需要讀取數(shù)據(jù)的次數(shù)
  return math.ceil(len(self.x_y) / self.batch_size)

 def preprocess_img(self, img_path):

  img = Image.open(img_path)
  resize_scale = self.img_size[0] / max(img.size[:2])
  img = img.resize((self.img_size[0], self.img_size[0]))
  img = img.convert('RGB')
  img = np.array(img)

  # 數(shù)據(jù)歸一化
  img = np.asarray(img, np.float32) / 255.0
  return img

 def __getitem__(self, idx):
  batch_x = self.x_y[idx * self.batch_size: (idx + 1) * self.batch_size, 0]
  batch_y = self.x_y[idx * self.batch_size: (idx + 1) * self.batch_size, 1:]
  batch_x = np.array([self.preprocess_img(img_path) for img_path in batch_x])
  batch_y = np.array(batch_y).astype(np.float32)
  print(batch_x.shape)
  return batch_x, batch_y
 #重寫的父類Sequence中的on_epoch_end方法,在每次迭代完后調(diào)用。
 def on_epoch_end(self):
  #每次迭代后重新打亂訓(xùn)練集數(shù)據(jù)
  np.random.shuffle(self.x_y)

在上面代碼中,__len __和__getitem __,是我們重寫的魔法方法,__len __是當(dāng)我們調(diào)用len(BaseSequence)函數(shù)時(shí)調(diào)用,這里我們返回(樣本總量/batch_size),供我們傳入fit_generator中的steps_per_epoch參數(shù);__getitem __可以讓對(duì)象實(shí)現(xiàn)迭代功能,這樣在將BaseSequence的對(duì)象傳入fit_generator中后,不斷執(zhí)行g(shù)enerator就可循環(huán)的讀取數(shù)據(jù)了。

舉個(gè)例子說(shuō)明一下getitem的作用:

class Animal:
 def __init__(self, animal_list):
  self.animals_name = animal_list

 def __getitem__(self, index):
  return self.animals_name[index]

animals = Animal(["dog","cat","fish"])
for animal in animals:
 print(animal)

輸出結(jié)果:

dog
cat
fish

并且使用Sequence類可以保證在多進(jìn)程的情況下,每個(gè)epoch中的樣本只會(huì)被訓(xùn)練一次。

以上這篇淺談keras通過(guò)model.fit_generator訓(xùn)練模型(節(jié)省內(nèi)存)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • tensorflow之自定義神經(jīng)網(wǎng)絡(luò)層實(shí)例

    tensorflow之自定義神經(jīng)網(wǎng)絡(luò)層實(shí)例

    今天小編就為大家分享一篇tensorflow之自定義神經(jīng)網(wǎng)絡(luò)層實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-02-02
  • Pandas實(shí)現(xiàn)DataFrame的簡(jiǎn)單運(yùn)算、統(tǒng)計(jì)與排序

    Pandas實(shí)現(xiàn)DataFrame的簡(jiǎn)單運(yùn)算、統(tǒng)計(jì)與排序

    本文主要介紹了Pandas實(shí)現(xiàn)DataFrame的簡(jiǎn)單運(yùn)算、統(tǒng)計(jì)與排序,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-03-03
  • 小結(jié)Python用fork來(lái)創(chuàng)建子進(jìn)程注意事項(xiàng)

    小結(jié)Python用fork來(lái)創(chuàng)建子進(jìn)程注意事項(xiàng)

    今天看到別人的源代碼中有 fork 子進(jìn)程來(lái)操作數(shù)據(jù)。但是由于 fork 之后,沒有及時(shí)的退出,導(dǎo)致系統(tǒng)中的Python進(jìn)程越來(lái)越多,子進(jìn)程越來(lái)越多了。
    2014-07-07
  • 最新評(píng)論