在PAI上, 使用TensorFlow讀取OSS文件
本文適合有一定TensorFlow基礎(chǔ), 且準(zhǔn)備使用PAI的同學(xué)閱讀
目錄
- 如何使用PAI上讀取OSS數(shù)據(jù)
- 如何使用PAI上寫(xiě)入數(shù)據(jù)到OSS
- 如何減少讀取的費(fèi)用開(kāi)支
- 使用OSS需要注意的問(wèn)題
1. 在PAI上讀取數(shù)據(jù)
Python不支持讀取oss的數(shù)據(jù), 故所有調(diào)用python Open() os.path.exist() 等文件, 文件夾操作的
函數(shù)的代碼都無(wú)法執(zhí)行.
如Scipy.misc.imread(),numpy.load() 等
那如何在PAI讀取數(shù)據(jù)呢, 通常我們采用兩種辦法.
- 如果只是簡(jiǎn)單的讀取一張圖片, 或者一個(gè)文本等, 可以使用tf.gfile下的函數(shù), 具體成員函數(shù)如下
tf.gfile.Copy(oldpath, newpath, overwrite=False) # 拷貝文件
tf.gfile.DeleteRecursively(dirname) # 遞歸刪除目錄下所有文件
tf.gfile.Exists(filename) # 文件是否存在
tf.gfile.FastGFile(name, mode='r') # 無(wú)阻塞讀取文件
tf.gfile.GFile(name, mode='r') # 讀取文件
tf.gfile.Glob(filename) # 列出文件夾下所有文件, 支持pattern
tf.gfile.IsDirectory(dirname) # 返回dirname是否為一個(gè)目錄
tf.gfile.ListDirectory(dirname) # 列出dirname下所有文件
tf.gfile.MakeDirs(dirname) # 在dirname下創(chuàng)建一個(gè)文件夾, 如果父目錄不存在, 會(huì)自動(dòng)創(chuàng)建父目錄. 如果
文件夾已經(jīng)存在, 且文件夾可寫(xiě), 會(huì)返回成功
tf.gfile.MkDir(dirname) # 在dirname處創(chuàng)建一個(gè)文件夾
tf.gfile.Remove(filename) # 刪除filename
tf.gfile.Rename(oldname, newname, overwrite=False) # 重命名
tf.gfile.Stat(dirname) # 返回目錄的統(tǒng)計(jì)數(shù)據(jù)
tf.gfile.Walk(top, inOrder=True) # 返回目錄的文件樹(shù)
具體的文檔可以參照這里(可能需要翻墻)
- 如果是一批一批的讀取文件, 一般會(huì)采用tf.WhoFileReader() 和 tf.train.batch() /
tf.train.shuffer_batch()
接下來(lái)會(huì)重點(diǎn)介紹常用的 tf.gfile.Glob, tf.gfile.FastGFile, tf.WhoFileReader() 和
tf.train.shuffer_batch()
讀取文件一般有兩步
- 獲取文件列表
- 讀取文件
如果是批量讀取, 還有第三步
- 創(chuàng)建batch
從代碼上手:
在使用PAI的時(shí)候, 通常需要在右側(cè)設(shè)置讀取目錄, 代碼文件等參數(shù), 這些參數(shù)都會(huì)通過(guò)--XXX的形式傳入
tf.flags可以提供了這個(gè)功能
import tensorflow as tf
FLAGS = tf.flags.FLAGS
# 前面的buckets, checkpointDir都是固定的, 不建議更改
tf.flags.DEFINE_string('buckets', 'oss://XXX', '訓(xùn)練圖片所在文件夾')
tf.flags.DEFINE_string('batch_size', '15', 'batch大小')
# 獲取文件列表
files = tf.gfile.Glob(os.path.join(FLAGS.buckets,'*.jpg')) # 如我想列出buckets下所有jpg文件路徑
接下來(lái)就分兩種情況了
- (小規(guī)模讀取時(shí)建議) tf.gfile.FastGfile()
for path in files:
file_content = tf.gfile.FastGFile(path, 'rb').read() # 一定記得使用rb讀取, 不然很多情況下都會(huì)報(bào)錯(cuò)
image = tf.image.decode_jpeg(file_content, channels=3) # 本教程以JPG圖片為例
- (大批量讀取時(shí)建議) tf.WhoFileReader()
reader = tf.WholeFileReader() # 實(shí)例化一個(gè)reader
fileQueue = tf.train.string_input_producer(files) # 創(chuàng)建一個(gè)供reader讀取的隊(duì)列
file_name, file_content = reader.read(fileQueue) # 使reader從隊(duì)列中讀取一個(gè)文件
image_content = tf.image.decode_jpeg(file_content, channels=3) # 講讀取結(jié)果解碼為圖片
label = XXX # 這里省略處理label的過(guò)程
batch = tf.train.shuffle_batch([label, image_content], batch_size=FLAGS.batch_size, num_threads=4,
capacity=1000 + 3 * FLAGS.batch_size, min_after_dequeue=1000)
sess = tf.Session() # 創(chuàng)建Session
tf.train.start_queue_runners(sess=sess) # 重要!!! 這個(gè)函數(shù)是啟動(dòng)隊(duì)列, 不加這句線程會(huì)一直阻塞
labels, images = sess.run(batch) # 獲取結(jié)果
現(xiàn)在解釋下其中重要的部分
- tf.train.string_input_producer, 這個(gè)是把files轉(zhuǎn)換成一個(gè)隊(duì)列, 并且需要 tf.train.start_queue_runners 來(lái)啟動(dòng)隊(duì)列
- tf.train.shuffle_batch 參數(shù)解釋
- batch_size 批大小, 每次運(yùn)行這個(gè)batch, 返回多少個(gè)數(shù)據(jù)
- num_threads 運(yùn)行線程數(shù), 在PAI上4個(gè)就好
- capacity 隨機(jī)取文件范圍, 比如你的數(shù)據(jù)集有10000個(gè)數(shù)據(jù), 你想從5000個(gè)數(shù)據(jù)中隨機(jī)取, capacity就設(shè)置成5000.
- min_after_dequeue 維持隊(duì)列的最小長(zhǎng)度, 這里只要注意不要大于capacity即可
2. 寫(xiě)入數(shù)據(jù)
1.直接使用tf.gfile.FastGFile()寫(xiě)入
tf.gfile.FastGFile(FLAGS.checkpointDir + 'example.txt', 'wb').write('hello world')
- 通過(guò)tf.gfile.Copy()拷貝
tf.gfile.Copy('./example.txt', FLAGS.checkpointDir + 'example.txt')
通過(guò)這兩種方法, 文件都會(huì)出現(xiàn)在 '輸出目錄/model/example.txt' 下
3. 費(fèi)用開(kāi)支
這里只討論讀取文件所需要的費(fèi)用開(kāi)支
原則上來(lái)說(shuō), PAI不跨區(qū)域讀取OSS是不收費(fèi)的, 但是OSS的API是收費(fèi)的. PAI在使用 tf.gile.Glob 的時(shí)候
會(huì)產(chǎn)生GET請(qǐng)求, 在寫(xiě)入tensorboard的時(shí)候, 也會(huì)產(chǎn)生PUT請(qǐng)求. 這兩種請(qǐng)求都是按次收費(fèi)的, 具體價(jià)格如下
標(biāo)準(zhǔn)型單價(jià): 0.01元/萬(wàn)次
低頻訪問(wèn)型單價(jià): 0.1元/萬(wàn)次
歸檔型單價(jià): 0.1元/萬(wàn)次
當(dāng)數(shù)據(jù)集有幾十萬(wàn)圖片, 通過(guò)tf.gile.Glob一次就需要幾毛錢(qián). 所以減少費(fèi)用開(kāi)支的方法就是減少GET請(qǐng)求次數(shù)
這里給出幾種解決思路
最好的解決思路, 把所有會(huì)使用到的數(shù)據(jù), 一并上傳傳到OSS, 然后使用tensorflow拷貝到運(yùn)行時(shí)目錄, 最后通過(guò)tensorflow讀取, 這樣是最節(jié)省開(kāi)支的.
通過(guò)tfrecords, 在本地, 提前把幾十上百?gòu)垐D片通過(guò)tfrecords存下來(lái), 這樣讀取的時(shí)候可以減少GET請(qǐng)求
- 把訓(xùn)練使用的圖片隨著代碼的壓縮包一起傳上去, 不走OSS讀取
三種方法都可以顯著的減少開(kāi)支.
4.使用中需要注意的
事實(shí)上, 每次讀取傳過(guò)來(lái)的地址就是 oss://你的buckets名字/XXX, 本以為不需要在PAI界面上 設(shè)置, 直接讀取這個(gè)目錄就好, 事實(shí)上并不如此.
PAI沒(méi)有權(quán)限讀取不在數(shù)據(jù)源目錄和輸出目錄下的文件, 所以在使用路徑前, 確保他們已經(jīng)在控制臺(tái)右側(cè)設(shè)置過(guò).
OSS路徑推薦使用
FLAGS.checkpointDir
FLAGS.summaryDIr
這樣的形式傳入, 經(jīng)過(guò)測(cè)試好像也只有這兩個(gè)目錄下有寫(xiě)權(quán)限
FLAGS.buckets下所有文件夾都有讀寫(xiě)權(quán)限