R-CNN原理详解与代码超详细讲解(二)--Alexnet模型和SVM模型

R-CNN原理详解与代码超详细讲解(二)–Alexnet模型对象和SVM模型对象

下面是R-CNN中Alexnet模型对象和SVM模型对象 代码
一、Alexnet模型对象

class AlexNet(object):
    def __init__(self, alexnet_mat_file_path=None, is_training=True, is_svm=False):
        self.image_height = config.IMAGE_HEIGHT  #图高,这里默认的是227
        self.image_width = config.IMAGE_WIDTH    #图宽,这里默认的是227
        self.image_channel = config.IMAGE_CHANNEL #图片的通道数,一般默认为3
        self.class_number = config.CLASS_NUMBER  #数据总类别,这里只用了17花数据集中两个花的样式,加上背景,所以这里是3
        if alexnet_mat_file_path is not None and os.path.exists(alexnet_mat_file_path):
            '''判断alexnet权重文件是否存在,如果存在,后续函数会进行掉用,同时会返回掉用结果'''
            self.load_train_weight = self.__reload_weights_and_biases_by_mat(alexnet_mat_file_path)
        else:
            self.load_train_weight = False
        self.train_weight = not self.load_train_weight #如果上述mat文件调用函数返回的是True,那么train_weight默认就是false
        if self.load_train_weight:
            print("Load network weights from AlexNet in ImageNet model!!!!")

        self.input_data = tf.placeholder(tf.float32, [None, self.image_height, self.image_width, self.image_channel],
                                         name='input')  
        self.logits = self.__build_network(input=self.input_data, output_dims=self.class_number,
                                           is_training=is_training, is_svm=is_svm) #通过alexnet网络得到的输出值

        if is_training:
            # 获取这个损失函数
            self.label = tf.placeholder(tf.float32, [None, self.class_number], name='label')
            # 构造损失函数
            self.__loss_layer(y_pred=self.logits, y_true=self.label)
            # 获取所有的损失函数
            self.total_loss = tf.losses.get_total_loss()
            tf.summary.scalar('total_loss', self.total_loss)
            # 获取准确率
            self.accuracy = self.__get_accuracy(y_pred=self.logits, y_true=self.label)

    def __build_network(self, input, output_dims, is_svm=False, scope='R-CNN', is_training=True, keep_prob=0.5):
        with tf.variable_scope(scope):
            # slim.arg_scope: 功能,对于某些API中(with语句块内部如果使用到这个API,才会生效),设置一个模型的输入参数(默认)
            conv_weights_regularizer = slim.l2_regularizer(0.005) if self.train_weight else None
            with slim.arg_scope([slim.conv2d, slim.fully_connected],
                                activation_fn=nn_ops.relu,
                                weights_initializer=tf.truncated_normal_initializer(0.0, 0.001),
                                weights_regularizer=slim.l2_regularizer(0.005)):
                with slim.arg_scope([slim.conv2d], weights_regularizer=conv_weights_regularizer):
                    with tf.variable_scope("Layer1"):
                        # 参数从左到右,分别:卷积输入的tensor对象、输出的通道数(卷积核数目)、窗口大小、窗口滑动步长大小、padding填充方式(卷积中默认为SAME)
                        net = slim.conv2d(input, 96, 11, 4, 'SAME', scope='conv',
                                          trainable=self.train_weight,
                                          weights_initializer=self.__get_weights_initializer("conv1"),
                                          biases_initializer=self.__get_biases_initializer("conv1"))
                        net = tf.nn.lrn(net, depth_radius=5, bias=1.0, alpha=0.0001, beta=0.75, name='lrn')
                        # 参数从左到右,分别:池化输入的tensor对象、窗口大小、窗口滑动步长大小、padding填充方式(池化中默认为VALID)
                        net = slim.max_pool2d(net, 3, 2, 'VALID', scope='pool')

                    with tf.variable_scope("Layer2"):
                        # 对96个通道的Image Tensor对象进行拆分为48 + 48通道的两个Net,分别做卷积操作
                        net1, net2 = tf.split(net, num_or_size_splits=2, axis=3, name='split')

                        # 分别做卷积+LRN+池化操作
                        with tf.variable_scope("branch1"):
                            net1 = slim.conv2d(net1, 128, 5, 1, scope='conv',
                                               trainable=self.train_weight,
                                               weights_initializer=self.__get_weights_initializer("conv2_1"),
                                               biases_initializer=self.__get_biases_initializer("conv2_1"))
                            net1 = tf.nn.lrn(net1, depth_radius=5, bias=1.0, alpha=0.0001, beta=0.75, name='lrn')
                            net1 = slim.max_pool2d(net1, 3, 2, 'VALID', scope='pool')
                        with tf.variable_scope("branch2"):
                            net2 = slim.conv2d(net2, 128, 5, 1, scope='conv',
                                               trainable=self.train_weight,
                                               weights_initializer=self.__get_weights_initializer("conv2_2"),
                                               biases_initializer=self.__get_biases_initializer("conv2_2"))
                            net2 = tf.nn.lrn(net2, depth_radius=5, bias=1.0, alpha=0.0001, beta=0.75, name='lrn')
                            net2 = slim.max_pool2d(net2, 3, 2, 'VALID', scope='pool')

                        # 合并两个分支的输出
                        net = tf.concat([net1, net2], axis=3, name='concat')

                    with tf.variable_scope("Layer3"):
                        net = slim.conv2d(net, 384, 3, 1, scope='conv',
                                          trainable=self.train_weight,
                                          weights_initializer=self.__get_weights_initializer("conv3"),
                                          biases_initializer=self.__get_biases_initializer("conv3"))

                    with tf.variable_scope("Layer4"):
                        # 对384个通道的Image Tensor对象进行拆分为192 + 192通道的两个Net,分别做卷积操作
                        net1, net2 = tf.split(net, num_or_size_splits=2, axis=3, name='split')

                        # 分别做卷积操作
                        with tf.variable_scope("branch1"):
                            net1 = slim.conv2d(net1, 192, 3, 1, scope='conv',
                                               trainable=self.train_weight,
                                               weights_initializer=self.__get_weights_initializer("conv4_1"),
                                               biases_initializer=self.__get_biases_initializer("conv4_1"))
                        with tf.variable_scope("branch2"):
                            net2 = slim.conv2d(net2, 192, 3, 1, scope='conv',
                                               trainable=self.train_weight,
                                               weights_initializer=self.__get_weights_initializer("conv4_2"),
                                               biases_initializer=self.__get_biases_initializer("conv4_2"))

                    with tf.variable_scope("Layer5"):
                        # 分别做卷积操作
                        with tf.variable_scope("branch1"):
                            net1 = slim.conv2d(net1, 128, 3, 1, scope='conv',
                                               trainable=self.train_weight,
                                               weights_initializer=self.__get_weights_initializer("conv5_1"),
                                               biases_initializer=self.__get_biases_initializer("conv5_1"))
                        with tf.variable_scope("branch2"):
                            net2 = slim.conv2d(net2, 128, 3, 1, scope='conv',
                                               trainable=self.train_weight,
                                               weights_initializer=self.__get_weights_initializer("conv5_2"),
                                               biases_initializer=self.__get_biases_initializer("conv5_2"))

                        # 合并两个卷积的结果
                        net = tf.concat([net1, net2], axis=3, name='concat')

                        # 池化
                        net = slim.max_pool2d(net, 3, 2, 'VALID', scope='pool')

                    with tf.variable_scope("Flatten_Layer"):
                        net = slim.flatten(net, scope='flatten')

                    with tf.variable_scope("Layer6"):
                        net = slim.fully_connected(net, 4096, activation_fn=tf.tanh, scope='fc',
                                                   weights_initializer=self.__get_weights_initializer("FC1"),
                                                   biases_initializer=self.__get_biases_initializer("FC1"))
                        net = slim.dropout(net, keep_prob=keep_prob, is_training=is_training, scope='dropout')
                    with tf.variable_scope("Layer7"):
                        net = slim.fully_connected(net, 4096, activation_fn=tf.tanh, scope='fc',
                                                   weights_initializer=self.__get_weights_initializer("FC2"),
                                                   biases_initializer=self.__get_biases_initializer("FC2"))

                    if is_svm:
                        # 因为SVM中,使用第7个全连接层作为网络的输出
                        return net
                    else:
                        # 返回的是预训练的模型结构
                        with tf.variable_scope("Layer8"):
                            net = slim.dropout(net, keep_prob=keep_prob, is_training=is_training, scope='dropout')
                            net = slim.fully_connected(net, output_dims, activation_fn=tf.nn.softmax, scope='fc')
                        return net

    def __loss_layer(self, y_pred, y_true):
        with tf.name_scope("CrossEntropy"):
            # 对预测值做一个截断的操作,防止预测值为0或者1的情况
            y_pred = tf.clip_by_value(y_pred,
                                      clip_value_min=tf.cast(1e-10, dtype=tf.float32),
                                      clip_value_max=tf.cast(1 - 1e-10, dtype=tf.float32))
            # 计算交叉熵
            cross_entropy = -tf.reduce_sum(y_true * tf.log(y_pred), axis=1)

            # 计算损失函数
            loss = tf.reduce_mean(cross_entropy)

            # 在tf中,有一个专门用于损失函数操作的模型
            # 添加损失
            tf.losses.add_loss(loss)

            # 添加可视化信息
            tf.summary.scalar('loss', loss)

    def __get_accuracy(self, y_pred, y_true):
        y_pred_maxs = tf.argmax(y_pred, 1)
        y_true_maxs = tf.argmax(y_true, 1)
        accuracy = tf.reduce_mean(tf.cast(tf.equal(y_pred_maxs, y_true_maxs), tf.float32))
        tf.summary.scalar('accuracy', accuracy)
        return accuracy

    def __get_weights_initializer(self, key):
        if self.load_train_weight and key in self.weights_dict:
            value = self.weights_dict[key]
            return tf.constant_initializer(value=value)
        else:
            return initializers.xavier_initializer()

    def __get_biases_initializer(self, key):
        if self.load_train_weight and key in self.bias_dict:
            value = self.bias_dict[key]
            return tf.constant_initializer(value=value)
        else:
            return init_ops.zeros_initializer()

    def __reload_weights_and_biases_by_mat(self, alexnet_mat_file_path):
        '''调用已经训练好的mat文件'''
        try:
            weights_dict = {} #存放mat文件中w权重的值
            bias_dict = {} #存放mat文件中b权重的值
            # 1. 加载这个mat的文件
            mdict = io.loadmat(alexnet_mat_file_path)

            # 2. 获取层次结构信息
            layers = mdict['layers'][0]

            # 3. 将卷积的参数保存
            weights_dict['conv1'] = layers[0][0][0][2][0][0]
            bias_dict['conv1'] = layers[0][0][0][2][0][1]

            # Mat文件中参数格式为[5,5,48,256]和[256,1]的形状,这里需要将其转换为[5,5,48,128]和[128,1]的两组参数
            w1, w2 = np.split(layers[4][0][0][2][0][0], 2, -1)
            b1, b2 = np.split(layers[4][0][0][2][0][1], 2, 0)
            weights_dict['conv2_1'] = w1
            weights_dict['conv2_2'] = w2
            bias_dict['conv2_1'] = b1
            bias_dict['conv2_2'] = b2

            weights_dict['conv3'] = layers[8][0][0][2][0][0]
            bias_dict['conv3'] = layers[8][0][0][2][0][1]

            # Mat文件中参数格式为[3,3,192,384]和[384,1]的形状,这里需要将其转换为[3,3,192,192]和[192,1]的两组参数
            w1, w2 = np.split(layers[10][0][0][2][0][0], 2, -1)
            b1, b2 = np.split(layers[10][0][0][2][0][1], 2, 0)
            weights_dict['conv4_1'] = w1
            weights_dict['conv4_2'] = w2
            bias_dict['conv4_1'] = b1
            bias_dict['conv4_2'] = b2

            # Mat文件中参数格式为[3,3,192,384]和[384,1]的形状,这里需要将其转换为[3,3,192,192]和[192,1]的两组参数
            w1, w2 = np.split(layers[12][0][0][2][0][0], 2, -1)
            b1, b2 = np.split(layers[12][0][0][2][0][1], 2, 0)
            weights_dict['conv5_1'] = w1
            weights_dict['conv5_2'] = w2
            bias_dict['conv5_1'] = b1
            bias_dict['conv5_2'] = b2

            # 加载全连接的参数
            weights_dict['FC1'] = np.reshape(layers[15][0][0][2][0][0], (-1, 4096))
            bias_dict['FC1'] = np.reshape(layers[15][0][0][2][0][1], -1)
            weights_dict['FC2'] = np.reshape(layers[17][0][0][2][0][0], (-1, 4096))
            bias_dict['FC2'] = np.reshape(layers[17][0][0][2][0][1], -1)

            # 4. 设置属性
            self.weights_dict = weights_dict
            self.bias_dict = bias_dict

            return True
        except:
            return False

二、SVM模型对象

class SVMModel(object):
    def __init__(self, is_training=True):
        # 获取标签信息
        check_directory(config.TRAIN_LABEL_DICT_FILE_PATH, created=False, error=True) #检查svm训练标签字典文件是否存在,如果不存在就直接报错,
        # 格式为:{'2': 1, '1': 2},key表示实际类别,value表示svm训练时候的标签类别
        class_name_2_index_dict = pickle.load(open(config.TRAIN_LABEL_DICT_FILE_PATH, 'rb'))
        self.labels = class_name_2_index_dict.values() #格式为:{[1,2]},是一个可迭代对象

        self.svm_model_dump_save_path = config.SVM_CHECKPOINT_FILE_PATH #具体哪个类别训练的svm模型存放路径
        self.label_2_models = {}

        if is_training:
            # 训练相关参数检查的操作
            self.svm_higher_features_save_path = config.TRAIN_SVM_HIGHER_FEATURES_DATA_FILE_PATH #svm训练所需要的cnn网络所提取的高阶特征,'shape': (10, 4097)
            for label in self.labels:
                check_directory(self.svm_higher_features_save_path.format(label), created=False, error=True)
            # 模型输出文件夹是否存在,如果不存在,进行创建
            check_directory(os.path.dirname(self.svm_model_dump_save_path))
        else:
            # 检查模型是否存在,如果存在,进行恢复加载的操作
            for label in self.labels:
                filename = self.svm_model_dump_save_path.format(label)
                check_directory(filename, created=False, error=True)
                self.label_2_models[label] = joblib.load(filename)

    def train(self):
        """
        对每个类别均进行模型训练
        :return:
        """
        for label in self.labels:
            print("Training type '{}' svm model .....".format(label))
            # 1. 加载数据
            data = np.load(self.svm_higher_features_save_path.format(label)) #shape:(10,4097),其中最后一列为标签y
            x, y = np.split(data, indices_or_sections=(np.shape(data)[1] - 1,), axis=1) #分割
            y = np.reshape(y, -1)
            print(np.shape(x), np.shape(y)) #(10, 4096) (10,)
            # 下面这行代码存在的主要意义是因为前面提取高阶特征的时候提取失败了
            y[0] = 1

            # 2. 模型构建
            algo = SVC(C=1.0, kernel='linear', random_state=28, max_iter=1000, probability=True)

            # 3. 模型训练
            algo.fit(x, y)

            # 4. 模型效果评估
            pred = algo.predict(x)
            print("SVM accuracy on training data:{}".format(metrics.accuracy_score(y, pred)))
            print("SVM confusion matrix on training data:\n{}".format(metrics.confusion_matrix(y, pred)))

            # 5. 模型持久化
            joblib.dump(algo, self.svm_model_dump_save_path.format(label))
            self.label_2_models[label] = algo

    def predict(self, x, label):
        """
        使用给定类别的模型进行数据预测操作
        :param x:
        :param label:
        :return:
        """
        if label in self.label_2_models:
            # 1. 加载模型
            algo = self.label_2_models[label]

            # 2. 结果预测
            return algo.predict(x)
        else:
            return None

    def predict_proba(self, x, label):
        """
        使用给定类别的模型进行数据预测操作
        :param x:
        :param label:
        :return:
        """
        if label in self.label_2_models:
            # 1. 加载模型
            algo = self.label_2_models[label]

            # 2. 结果预测
            return algo.predict_proba(x)
        else:
            return None

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
HMM-SVM是一种序列分类方法,它结合了隐马尔可夫模型(HMM)和支持向量机(SVM)的优点,可以用于解决序列分类问题。下面我们将使用Python编写一个简单的HMM-SVM代码,以便更好地理解这种方法。 首先,我们需要安装必要的库,包括numpy、scikit-learn和hmmlearn。可以使用以下命令进行安装: ``` pip install numpy scikit-learn hmmlearn ``` 接下来,我们将使用一个简单的序列分类问题来演示HMM-SVM的使用。假设我们有一个数据集,其中包含5个序列,每个序列包含3个观测值。我们需要将这些序列分为两个类别:正类和负类。我们将首先生成这个数据集: ```python import numpy as np # 生成数据集 np.random.seed(0) X = np.concatenate([np.random.randn(3, 2) + [i, i] for i in range(5)]) y = np.array([0, 1, 0, 1, 0] * 5) ``` 这里我们使用了numpy库,生成了一个大小为(15, 2)的维数组X,其中包含了5个序列,每个序列包含了3个观测值。我们还生成了一个大小为(15,)的一维数组y,其中包含了每个序列所属的类别。 接下来,我们将使用hmmlearn库中的GaussianHMM类来训练一个隐马尔可夫模型。我们将使用5个状态和对数似然函数来训练模型: ```python from hmmlearn.hmm import GaussianHMM # 训练HMM模型 model = GaussianHMM(n_components=5, verbose=True) model.fit(X) ``` 这里我们使用了GaussianHMM类,并传入了n_components参数,表示模型中的状态数。我们还将verbose参数设置为True,以便输出训练过程中的详细信息。我们使用fit方法来训练模型。 接下来,我们将使用scikit-learn库中的SVC类来训练一个支持向量机分类器。我们将使用线性核函数和C参数为1.0: ```python from sklearn.svm import SVC # 训练SVM分类器 svm = SVC(kernel='linear', C=1.0) svm.fit(model.transmat_, y) ``` 这里我们使用了SVC类,并传入了kernel和C参数。我们使用fit方法来训练模型。 最后,我们可以使用训练好的HMM和SVM模型来进行预测。我们将使用predict方法来预测每个序列所属的类别: ```python # 预测序列类别 y_pred = svm.predict(model.transmat_) print(y_pred) ``` 这里我们使用了predict方法,并传入了转移矩阵model.transmat_作为输入。输出结果为: ``` [0 1 0 1 0] ``` 这里我们使用HMM-SVM方法成功地将5个序列分为了两个类别。完整代码如下: ```python import numpy as np from hmmlearn.hmm import GaussianHMM from sklearn.svm import SVC # 生成数据集 np.random.seed(0) X = np.concatenate([np.random.randn(3, 2) + [i, i] for i in range(5)]) y = np.array([0, 1, 0, 1, 0] * 5) # 训练HMM模型 model = GaussianHMM(n_components=5, verbose=True) model.fit(X) # 训练SVM分类器 svm = SVC(kernel='linear', C=1.0) svm.fit(model.transmat_, y) # 预测序列类别 y_pred = svm.predict(model.transmat_) print(y_pred) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

进我的收藏吃灰吧~~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值