Tensorflow tflearn 编写RCNN(转+修改)

原帖: http://www.cnblogs.com/edwardbi/p/5647522.html

原帖GitHub: https://github.com/edwardbi/DeepLearningModels/tree/master/RCNN

少量代码需要修改 尤其是缩进的问题!!

修改后的Github:https://github.com/UncleLLD/RCNN/   修改地方可以参考下,也许是自己的版本问题,有过一些报错。

最后以修改缩进以及python3版本运行完成重现

 

 

两周多的努力总算写出了RCNN的代码,这段代码非常有意思,并且还顺带复习了几个Tensorflow应用方面的知识点,故特此总结下,带大家分享下经验。理论方面,RCNN的理论教程颇多,这里我不在做详尽说明,有兴趣的朋友可以看看这个博客以了解大概。

系统概况

RCNN的逻辑基于Alexnet模型。为增加模型的物体辨识率,在图片未经CNN处理前,先由传统算法(文中所用算法为Selective Search算法)取得大概2000左右的疑似物品框。之后,这些疑似框被导入CNN系统中以取得输出层前一层的特征后,由训练好的svm来区分物体。这之中,比较有意思的部分包括了对经过ImageNet训练后的Alexnet的fine tune,对fine tune后框架里输出层前的最后一层特征点的提取以及训练svm分类器。下面,让我们来看看如何实现这个模型吧!

代码解析

为方便编写,这里应用了tflearn库作为tensorflow的一个wrapper来编写Alexnet,关于tflearn,具体资料请点击这里查看其官网。

那么下面,让我们先来看看系统流程:

第一步,训练Alexnet,这里我们运用的是github上tensorflow-alexnet项目。该项目将Alexnet运用在学习flower17数据库上,说白了也就是区分不同种类的花的项目。github提供的代码所有功能作者都有认真的写出,不过在main的写作以及对模型是否支持在断点处继续训练等问题上作者并没写明,这里贴上我的代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

def train(network, X, Y):

    # Training

    model = tflearn.DNN(network, checkpoint_path='model_alexnet',

                        max_checkpoints=1, tensorboard_verbose=2, tensorboard_dir='output')

    # 这里增加了读取存档的模式。如果已经有保存了的模型,我们当然就读取它然后继续

    # 训练了啊!

    if os.path.isfile('model_save.model'):

        model.load('model_save.model')

    model.fit(X, Y, n_epoch=100, validation_set=0.1, shuffle=True,

              show_metric=True, batch_size=64, snapshot_step=200,

              snapshot_epoch=False, run_id='alexnet_oxflowers17'# epoch = 1000

    # Save the model

    # 这里是保存已经运算好了的模型

    model.save('model_save.model')

 同时,我们希望可以检测模型是否运作正常。以下是检测Alexnet用代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

# 预处理图片函数:

# ------------------------------------------------------------------------------------------------

# 首先,读取图片,形成一个Image文件

def load_image(img_path):

    img = Image.open(img_path)

    return img

# 将Image文件给修改成224 * 224的图片大小(当然,RGB三个频道我们保持不变)

def resize_image(in_image, new_width, new_height, out_image=None,

                 resize_mode=Image.ANTIALIAS):

    img = in_image.resize((new_width, new_height), resize_mode)

    if out_image:

        img.save(out_image)

    return img

# 将Image加载后转换成float32格式的tensor

def pil_to_nparray(pil_image):

    pil_image.load()

    return np.asarray(pil_image, dtype="float32")

 

# 网络框架函数:

# ------------------------------------------------------------------------------------------------

def create_alexnet(num_classes):

    # Building 'AlexNet'

    network = input_data(shape=[None2242243])

    network = conv_2d(network, 9611, strides=4, activation='relu')

    network = max_pool_2d(network, 3, strides=2)

    network = local_response_normalization(network)

    network = conv_2d(network, 2565, activation='relu')

    network = max_pool_2d(network, 3, strides=2)

    network = local_response_normalization(network)

    network = conv_2d(network, 3843, activation='relu')

    network = conv_2d(network, 3843, activation='relu')

    network = conv_2d(network, 2563, activation='relu')

    network = max_pool_2d(network, 3, strides=2)

    network = local_response_normalization(network)

    network = fully_connected(network, 4096, activation='tanh')

    network = dropout(network, 0.5)

    network = fully_connected(network, 4096, activation='tanh')

    network = dropout(network, 0.5)

    network = fully_connected(network, num_classes, activation='softmax')

    network = regression(network, optimizer='momentum',

                         loss='categorical_crossentropy',

                         learning_rate=0.001)

    return network

 

# 我们就是用这个函数来推断输入图片的类别的

def predict(network, modelfile,images):

    model = tflearn.DNN(network)

    model.load(modelfile)

    return model.predict(images)

 

if __name__ == '__main__':

    img_path = 'testimg7.jpg'

    imgs = []

    img = load_image(img_path)

    img = resize_image(img, 224224)

    imgs.append(pil_to_nparray(img))

    net = create_alexnet(17)

    predicted = predict(net, 'model_save.model',imgs)

    print(predicted)

    

 到此为止,我们跟RCNN还没有直接的关系。不过,值得注意的是,我们之前保存的那个训练模型model_save.model文件就是我们预训练的Alexnet。那么下面,我们开始正式制作RCNN系统了,让我们先编写传统的框架proposal代码吧。

鉴于文中运用的算法是selective search, 对这个算法我个人没有太接触过,所以从头编写非常耗时。这里我偷了个懒,运用python现成的库selectivesearch去完成,那么,预处理代码的重心就在另一个概念上了,即IOU, interection or union概念。这个概念之所以在这里很有用是因为一张图片我们人为的去标注往往只为途中的某一样物体进行了标注,其余的我们全部算作背景了。在这个概念下,如果电脑一次性选择了许多可能物品框,我们如何决定哪个框对应这物体呢?对于完全不重叠的方框我们自然认为其标注的不是物体而是背景,但是对于那些重叠的方框怎么分类呢?我们这里便使用了IOU概念,即重叠数值超过一个阀门数值我们便将其标注为该物体类别,其他情况下我们均标注该方框为背景。更加详细的讲解请点击这里

那么在代码上我们如何实现这个IOU呢?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

# IOU Part 1

def if_intersection(xmin_a, xmax_a, ymin_a, ymax_a, xmin_b, xmax_b, ymin_b, ymax_b):

    if_intersect = False

    # 通过四条if来查看两个方框是否有交集。如果四种状况都不存在,我们视为无交集

    if xmin_a < xmax_b <= xmax_a and (ymin_a < ymax_b <= ymax_a or ymin_a <= ymin_b < ymax_a):

        if_intersect = True

    elif xmin_a <= xmin_b < xmax_a and (ymin_a < ymax_b <= ymax_a or ymin_a <= ymin_b < ymax_a):

        if_intersect = True

    elif xmin_b < xmax_a <= xmax_b and (ymin_b < ymax_a <= ymax_b or ymin_b <= ymin_a < ymax_b):

        if_intersect = True

    elif xmin_b <= xmin_a < xmax_b and (ymin_b < ymax_a <= ymax_b or ymin_b <= ymin_a < ymax_b):

        if_intersect = True

    else:

        return False

    # 在有交集的情况下,我们通过大小关系整理两个方框各自的四个顶点, 通过它们得到交集面积

    if if_intersect == True:

        x_sorted_list = sorted([xmin_a, xmax_a, xmin_b, xmax_b])

        y_sorted_list = sorted([ymin_a, ymax_a, ymin_b, ymax_b])

        x_intersect_w = x_sorted_list[2- x_sorted_list[1

        y_intersect_h = y_sorted_list[2- y_sorted_list[1]

        area_inter = x_intersect_w * y_intersect_h

        return area_inter

 

# IOU Part 2

def IOU(ver1, vertice2):

    # vertices in four points

    # 整理输入顶点

    vertice1 = [ver1[0], ver1[1], ver1[0]+ver1[2], ver1[1]+ver1[3]]

    area_inter = if_intersection(vertice1[0], vertice1[2], vertice1[1], vertice1[3], vertice2[0], vertice2[2], vertice2[1], vertice2[3])

    # 如果有交集,计算IOU

    if area_inter:

        area_1 = ver1[2* ver1[3

        area_2 = vertice2[4* vertice2[5

        iou = float(area_inter) / (area_1 + area_2 - area_inter)

        return iou

    return False

 之后,我们便可以在fine tune Alexnet时以0.5为IOU的threthold, 并在训练SVM时以0.3为threthold。达成该思维的函数如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

# Read in data and save data for Alexnet

def load_train_proposals(datafile, num_clss, threshold = 0.5, svm = False, save=False, save_path='dataset.pkl'):

    train_list = open(datafile,'r')

    labels = []

    images = []

    for line in train_list:

        tmp = line.strip().split(' ')

        # tmp0 = image address

        # tmp1 = label

        # tmp2 = rectangle vertices

        img = skimage.io.imread(tmp[0])

        # python的selective search函数

        img_lbl, regions = selectivesearch.selective_search(img, scale=500, sigma=0.9, min_size=10)

        candidates = set()

        for in regions:

        # excluding same rectangle (with different segments)

            # 剔除重复的方框

            if r['rect'in candidates:

                continue

            # 剔除太小的方框

        if r['size'] < 220:

                continue

        # resize to 224 * 224 for input

            # 重整方框的大小

            proposal_img, proposal_vertice = clip_pic(img, r['rect'])

        # Delete Empty array

            # 如果截取后的图片为空,剔除

        if len(proposal_img) == 0:

            continue

            # Ignore things contain 0 or not C contiguous array

        x, y, w, h = r['rect']

        # 长或宽为0的方框,剔除

            if == 0 or == 0:

            continue

            # Check if any 0-dimension exist

            # image array的dim里有0的,剔除

        [a, b, c] = np.shape(proposal_img)

        if == 0 or == 0 or == 0:

        continue

        im = Image.fromarray(proposal_img)

        resized_proposal_img = resize_image(im, 224224)

        candidates.add(r['rect'])

        img_float = pil_to_nparray(resized_proposal_img)

            images.append(img_float)

            # 计算IOU

        ref_rect = tmp[2].split(',')

        ref_rect_int = [int(i) for in ref_rect]

            iou_val = IOU(ref_rect_int, proposal_vertice)

            # labels, let 0 represent default class, which is background

        index = int(tmp[1])

        if svm == False:

                label = np.zeros(num_clss+1)

                if iou_val < threshold:

                    label[0= 1

                else:

                    label[index] = 1

                labels.append(label)

        else:

            if iou_val < threshold:

            labels.append(0)

        else:

            labels.append(index)

    if save:

        pickle.dump((images, labels), open(save_path, 'wb'))

    return images, labels

 需要注意的是,这里输入参数的svm当为True时我们便不需要用one hot的方式表达label了。

在预处理了输入图片后,我们需要用预处理后的图片集来fine tune Alexnet。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

# Use a already trained alexnet with the last layer redesigned

# 这里定义了我们的Alexnet的fine tune框架。按照原文,我们需要丢弃alexnet的最后一层,即softmax

# 然后换上一层新的softmax专门针对新的预测的class数+1(因为多出了个背景class)。具体方法为设

# restore为False,这样在最后一层softmax处,我不restore任何数值。

def create_alexnet(num_classes, restore=False):

    # Building 'AlexNet'

    network = input_data(shape=[None2242243])

    network = conv_2d(network, 9611, strides=4, activation='relu')

    network = max_pool_2d(network, 3, strides=2)

    network = local_response_normalization(network)

    network = conv_2d(network, 2565, activation='relu')

    network = max_pool_2d(network, 3, strides=2)

    network = local_response_normalization(network)

    network = conv_2d(network, 3843, activation='relu')

    network = conv_2d(network, 3843, activation='relu')

    network = conv_2d(network, 2563, activation='relu')

    network = max_pool_2d(network, 3, strides=2)

    network = local_response_normalization(network)

    network = fully_connected(network, 4096, activation='tanh')

    network = dropout(network, 0.5)

    network = fully_connected(network, 4096, activation='tanh')

    network = dropout(network, 0.5)

    network = fully_connected(network, num_classes, activation='softmax', restore=restore)

    network = regression(network, optimizer='momentum',

                         loss='categorical_crossentropy',

                         learning_rate=0.001)

    return network

 

# 这里,我们的训练从已经训练好的alexnet开始,即model_save.model开始读取。在训练后,我们

# 将训练资料收录到fine_tune_model_save.model里

def fine_tune_Alexnet(network, X, Y):

    # Training

    model = tflearn.DNN(network, checkpoint_path='rcnn_model_alexnet',

                        max_checkpoints=1, tensorboard_verbose=2, tensorboard_dir='output_RCNN')

    if os.path.isfile('fine_tune_model_save.model'):

    print("Loading the fine tuned model")

        model.load('fine_tune_model_save.model')

    elif os.path.isfile('model_save.model'):

    print("Loading the alexnet")

    model.load('model_save.model')

    else:

    print("No file to load, error")

        return False

    model.fit(X, Y, n_epoch=10, validation_set=0.1, shuffle=True,

              show_metric=True, batch_size=64, snapshot_step=200,

              snapshot_epoch=False, run_id='alexnet_rcnnflowers2'# epoch = 1000

    # Save the model

    model.save('fine_tune_model_save.model')

 运用这两个函数可完成对Alexnet的fine tune。到此为止,我们完成了对Alexnet的直接运用,接下来,我们需要读取alexnet最后一层特征并用以训练svm。那么,我们怎么取得图片的feature呢?方法很简单,我们减去输出层即可。代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

# Use a already trained alexnet with the last layer redesigned

def create_alexnet(num_classes, restore=False):

    # Building 'AlexNet'

    network = input_data(shape=[None2242243])

    network = conv_2d(network, 9611, strides=4, activation='relu')

    network = max_pool_2d(network, 3, strides=2)

    network = local_response_normalization(network)

    network = conv_2d(network, 2565, activation='relu')

    network = max_pool_2d(network, 3, strides=2)

    network = local_response_normalization(network)

    network = conv_2d(network, 3843, activation='relu')

    network = conv_2d(network, 3843, activation='relu')

    network = conv_2d(network, 2563, activation='relu')

    network = max_pool_2d(network, 3, strides=2)

    network = local_response_normalization(network)

    network = fully_connected(network, 4096, activation='tanh')

    network = dropout(network, 0.5)

    network = fully_connected(network, 4096, activation='tanh')

    network = regression(network, optimizer='momentum',

                         loss='categorical_crossentropy',

                         learning_rate=0.001)

    return network

 在得到features后,我们需要训练SVM。为何要训练SVM呢?直接用CNN的softmax就好不就是么?这个问题在之前提及的博客里有提及。简而言之,SVM适用于小样本训练,这里这么做可以提高准确率。训练SVM的代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

# Construct cascade svms

def train_svms(train_file_folder, model):

    # 这里,我们将不同的训练集合分配到不同的txt文件里,每一个文件只含有一个种类

    listings = os.listdir(train_file_folder)

    svms = []

    for train_file in listings:

        if "pkl" in train_file:

        continue

        # 得到训练单一种类SVM的数据。

        X, Y = generate_single_svm_train(train_file_folder+train_file)

        train_features = []

        for in X:

            feats = model.predict([i])

            train_features.append(feats[0])

    print("feature dimension")

        print(np.shape(train_features))

        # 这里建立一个Cascade的SVM以区分所有物体

        clf = svm.LinearSVC()

        print("fit svm")

        clf.fit(train_features, Y)

    svms.append(clf)

    return svms

 在识别物体的时候,我们该怎么做呢?首先,我们通过一下函数得到输入图片的疑似物体框:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

def image_proposal(img_path):

    img = skimage.io.imread(img_path)

    img_lbl, regions = selectivesearch.selective_search(

                       img, scale=500, sigma=0.9, min_size=10)

    candidates = set()

    images = []

    vertices = []

    for in regions:

    # excluding same rectangle (with different segments)

        if r['rect'in candidates:

            continue

    if r['size'] < 220:

            continue

    # resize to 224 * 224 for input

        proposal_img, proposal_vertice = prep.clip_pic(img, r['rect'])

        # Delete Empty array

    if len(proposal_img) == 0:

        continue

        # Ignore things contain 0 or not C contiguous array

    x, y, w, h = r['rect']

    if == 0 or == 0:

        continue

        # Check if any 0-dimension exist

    [a, b, c] = np.shape(proposal_img)

    if == 0 or == 0 or == 0:

        continue

    im = Image.fromarray(proposal_img)

    resized_proposal_img = resize_image(im, 224224)

    candidates.add(r['rect'])

    img_float = pil_to_nparray(resized_proposal_img)

        images.append(img_float)

        vertices.append(r['rect'])

    return images, vertices

 该过程与预处理中函数类似,不过更简单,因为我们不需要考虑对应的label了。之后,我们将这些图片一个一个的输入网络以得到相对输出(其实可以一起做,不过我的电脑总是kill了,可能是内存或者其他问题吧),最后,应用cascaded的SVM就可以得到预测结果了。

大家对于试验结果一定很好奇。以下结果是对比了Alexnet和RCNN的运行结果。

首先,让我们来看看对于以下图片的结果:

对它的分析结果如下:在Alexnet的情况下,得到了以下数据:

判断为第四类花。实际结果在flower 17数据库中是最后一类,也就是第17类花。这里,第17类花的可能性仅次于第四类,为34%。那么,RCNN的结果如何呢?我们看下图:

显而易见,RCNN的正确率(1类)非常之高。

 

 

下面是本人的实践过程:

1.环境: Tensorflow version 0.7.1. Python version 2.7 ,scikit-learn dev distribution package.以及安装 tflearn( github at https://github.com/tflearn/tflearn),还记得要安装 selective search , pip install selectivesearch.

Training Input: 17 flowers 数据集下载: https://github.com/ck196/tensorflow-alexnet. 下载完毕后将数据集保存到本文GitHub位置处作为网络的预训练数据。

2.运行代码:终端目录进入github文件

  1.运行train_alexnet.py 训练alexnet网络,会生成model_save.model等文件,注意由于tf的版本不同,生成的文件数量也不一样,大致都是model文件

  2.运行fine_tune_RCNN.py文件,使用文件夹中自带的2flowers数据库微调,按照原作者的代码,这里会报错no file to load, error。在看相关解答后

原因是tflearn的save模型在tensorflow最新版本上无法正确保存成tflearn文件,而是保存成了三个文件(.data-0000-of-00001/index/meta)。
将fine_tune_RCNN中的fine_tune_Alexnet函数进行如下修改,会运行成功。
  3.运行RCNN_output.py文件分类图像

# 工程内容 这个程序是基于tensorflowtflearn库实现部分RCNN功能。 # 开发环境 windows10 + python3.5 + tensorflow1.2 + tflearn + cv2 + scikit-learn # 数据集 采用17flowers据集, 官网下载:http://www.robots.ox.ac.uk/~vgg/data/flowers/17/ # 程序说明 1、setup.py---初始化路径 2、config.py---配置 3、tools.py---进度条和显示带框图像工具 4、train_alexnet.py---大数据集预训练Alexnet网络,140个epoch左右,bitch_size为64 5、preprocessing_RCNN.py---图像的处理(选择性搜索、数据存取等) 6、selectivesearch.py---选择性搜索源码 7、fine_tune_RCNN.py---小数据集微调Alexnet 8、RCNN_output.py---训练SVM并测试RCNN(测试的时候测试图片选择第7、16类中没有参与训练的,单朵的花效果好,因为训练用的都是单朵的) # 文件说明 1、train_list.txt---预训练数据,数据在17flowers文件夹中 2、fine_tune_list.txt---微调数据2flowers文件夹中 3、1.png---直接用选择性搜索的区域划分 4、2.png---通过RCNN后的区域划分 # 程序问题 1、由于数据集小的原因,在微调时候并没有像论文一样按一个bitch32个正样本,128个负样本输入,感觉正样本过少; 2、还没有懂最后是怎么给区域打分的,所有非极大值抑制集合canny算子没有进行,待续; 3、对选择的区域是直接进行缩放的; 4、由于数据集合论文采用不一样,但是微调和训练SVM时采用的IOU阈值一样,有待调参。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

uncle_ll

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值