回归caffe的测试标签,不允许使用浮点数?

12

我正在使用caffe进行回归,我的test.txttrain.txt文件内容如下:

/home/foo/caffe/data/finetune/flickr/3860781056.jpg 2.0  
/home/foo/caffe/data/finetune/flickr/4559004485.jpg 3.6  
/home/foo/caffe/data/finetune/flickr/3208038920.jpg 3.2  
/home/foo/caffe/data/finetune/flickr/6170430622.jpg 4.0  
/home/foo/caffe/data/finetune/flickr/7508671542.jpg 2.7272

我的问题是,似乎caffe不允许使用像2.0这样的浮点标签。例如,当我在读取时使用浮点标签,比如'test.txt'文件中,caffe只会识别

总共1张图像

这是错误的。

但是,当我将文件中的2.0更改为2,并更改后面的行时,caffe现在给出了

总共2张图像

暗示浮点标签是问题的原因。

有人能帮我解决这个问题吗?我一定需要使用浮点标签进行回归,那么有人知道解决方法吗?提前致谢。

编辑 对于任何遇到类似问题的人,使用caffe训练Lenet与CSV数据可能会有所帮助。感谢@Shai。


“reading” 是什么意思?您是否使用 convert_imageset 工具? - Shai
4个回答

24
当使用图像数据集输入层(使用lmdbleveldb后端)时,Caffe每个输入图像仅支持一个整数标签。
如果您想进行回归,并使用浮点标签,则应尝试使用HDF5数据层。例如,请参见this question
在Python中,您可以使用h5py包创建hdf5文件。
import h5py, os
import caffe
import numpy as np

SIZE = 224 # fixed size to all images
with open( 'train.txt', 'r' ) as T :
    lines = T.readlines()
# If you do not have enough memory split data into
# multiple batches and generate multiple separate h5 files
X = np.zeros( (len(lines), 3, SIZE, SIZE), dtype='f4' ) 
y = np.zeros( (len(lines),1), dtype='f4' )
for i,l in enumerate(lines):
    sp = l.split(' ')
    img = caffe.io.load_image( sp[0] )
    img = caffe.io.resize( img, (SIZE, SIZE, 3) ) # resize to fixed size
    # you may apply other input transformations here...
    # Note that the transformation should take img from size-by-size-by-3 and transpose it to 3-by-size-by-size
    # for example
    # transposed_img = img.transpose((2,0,1))[::-1,:,:] # RGB->BGR
    X[i] = transposed_img
    y[i] = float(sp[1])
with h5py.File('train.h5','w') as H:
    H.create_dataset( 'X', data=X ) # note the name X given to the dataset!
    H.create_dataset( 'y', data=y ) # note the name y given to the dataset!
with open('train_h5_list.txt','w') as L:
    L.write( 'train.h5' ) # list all h5 files you are going to use

一旦您已经拥有了所有的h5文件以及列出它们的相应测试文件,您可以在train_val.prototxt中添加一个HDF5输入层:
 layer {
   type: "HDF5Data"
   top: "X" # same name as given in create_dataset!
   top: "y"
   hdf5_data_param {
     source: "train_h5_list.txt" # do not give the h5 files directly, but the list.
     batch_size: 32
   }
   include { phase:TRAIN }
 }

澄清:
当我说“caffe只支持每个输入图像一个整数标签”时,我并不是指leveldb/lmdb容器有限制,而是指caffe工具,特别是convert_imageset工具。
经过仔细检查,似乎caffe将类型为Datum的数据存储在leveldb/lmdb中,而这种类型的“label”属性被定义为整数(请参见caffe.proto),因此使用caffe接口到leveldb/lmdb时,您受到每个图像一个int32标签的限制。

1
感谢@Shai提供这么详细的答案。我会尝试并回报结果。再次感谢。 - Deven
1
@Deven,请将这些问题链接起来,以便查看每个来源的人可以快速高效地找到所有相关答案。 - Shai
1
这行代码:y = np.zeros( (1,len(lines)), dtype='f4' ) 应该改为 y = np.zeros( (len(lines)), dtype='f4' ),对吗? - unicorn_poet
如果按照你在答案中提到的方式去做,会抛出异常,因为如果i>0,则无法对y[i]进行索引。无论如何,我已经在这里发布了我的实际问题,以防您想要查看:http://stackoverflow.com/questions/33676656/caffe-hdf5-not-learning - unicorn_poet
1
@simplename 这段代码与此答案中的代码相同,只是 y[i] 应该是2D(实际上是3D,通道维度为1),而不是浮点标量。 - Shai
显示剩余24条评论

3

Shai的答案已经涵盖了将浮点标签保存为HDF5格式的内容。如果需要或更喜欢使用LMDB,以下是从浮点数据创建LMDB的代码片段(改编自 GitHub评论):

import lmdb
import caffe
def scalars_to_lmdb(scalars, path_dst):

    db = lmdb.open(path_dst, map_size=int(1e12))

    with db.begin(write=True) as in_txn:    
        for idx, x in enumerate(scalars):            
            content_field = np.array([x])
            # get shape (1,1,1)
            content_field = np.expand_dims(content_field, axis=0)
            content_field = np.expand_dims(content_field, axis=0)
            content_field = content_field.astype(float)

            dat = caffe.io.array_to_datum(content_field)
            in_txn.put('{:0>10d}'.format(idx) dat.SerializeToString())
    db.close()

很抱歉,使用caffe.io.array_to_datum存在问题,因为datum中的label字段被定义为整数 - Shai
1
@Shai,是的,真实数据保存在datum的数据字段中。这需要分别为输入和真实数据生成单独的lmdb。 - ypx

2
我最终采用了转置、通道顺序更改以及使用无符号整数而非浮点数来获得结果。建议从HDF5文件中读取图像,以确保其正确显示。
首先将图像作为无符号整数读取: img = np.array(Image.open('images/' + image_name), dtype=np.uint8) 然后将通道顺序从RGB更改为BGR: img = img[:, :, ::-1] 最后,从高度x宽度x通道切换到通道x高度x宽度: img = img.transpose((2, 0, 1)) 仅仅更改形状将会破坏您的图像并破坏您的数据!
要读回图像:
with h5py.File(h5_filename, 'r') as hf:
    images_test = hf.get('images')
    targets_test = hf.get('targets')
    for i, img in enumerate(images_test):
        print(targets_test[i])
        from skimage.viewer import ImageViewer
        viewer = ImageViewer(img.reshape(SIZE, SIZE, 3))
        viewer.show()

这是我编写的一个脚本,用于处理自动驾驶汽车任务中的两个标签(转向和速度):https://gist.github.com/crizCraig/aa46105d34349543582b177ae79f32f0


1
除了上面@Shai's answer提到的,我写了一个支持float类型标签的MultiTaskData层。
它的主要思想是将标签存储在Datumfloat_data字段中,MultiTaskDataLayer将根据net.prototxt中设置的task_numlabel_dimension值解析它们作为任意数量任务的标签。相关文件包括:caffe.protomultitask_data_layer.hpp/cppio.hpp/cpp
您可以轻松地将此层添加到自己的caffe中,并像这样使用它(这是一个面部表情标签分布学习任务的示例,在其中"exp_label"可以是浮点类型向量,例如[0.1, 0.1, 0.5, 0.2, 0.1]表示面部表情(5类)的概率分布)。
    name: "xxxNet"
    layer {
        name: "xxx"
        type: "MultiTaskData"
        top: "data"
        top: "exp_label"
        data_param { 
            source: "expression_ld_train_leveldb"   
            batch_size: 60 
            task_num: 1
            label_dimension: 8
        }
        transform_param {
            scale: 0.00390625
            crop_size: 60
            mirror: true
        }
        include:{ phase: TRAIN }
    }
    layer { 
        name: "exp_prob" 
        type: "InnerProduct"
        bottom: "data"  
        top: "exp_prob" 
        param {
            lr_mult: 1
            decay_mult: 1
        }
        param {
            lr_mult: 2
            decay_mult: 0
        }
        inner_product_param {
            num_output: 8
            weight_filler {
            type: "xavier"
            }    
        bias_filler {      
            type: "constant"
            }  
        }
    }
    layer {  
        name: "exp_loss"  
        type: "EuclideanLoss"  
        bottom: "exp_prob" 
        bottom: "exp_label"
        top: "exp_loss"
        include:{ phase: TRAIN }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接