Advertisement

TinyMind人民币面值&冠字号编码识别挑战赛 - 热身赛

阅读量:

TinyMind人民币面值&冠字号编码识别挑战赛 - 热身赛

1 数据探索、清洗

1.1 标签

  • 读取标签csv文件
复制代码
    train_lables_face_value_path = os.path.join(DATASET, TRAIN_LABELS_FACE_VALUE_FILE)
    df_rmb_face_value_labels = pd.read_csv(train_lables_face_value_path,
                                       index_col=None, header=0, delimiter=", ")
  • 统计
复制代码
    print("*" * 10)
    print(df_rmb_face_value_labels.head(10))
    print("*" * 10)
    print(df_rmb_face_value_labels.columns)
    print("*" * 10)
    print(df_rmb_face_value_labels.dtypes)
    print("*" * 10)
    print(df_rmb_face_value_labels.info())
    print("*" * 10)
    print(df_rmb_face_value_labels["label"].describe())
复制代码
    **********
           name  label
    0  013MNV9B.jpg  100.0
    1  016ETNGG.jpg   50.0
    2  018SUTBA.jpg    0.1
    3  0192G5IC.jpg    5.0
    4  01953EH7.jpg  100.0
    5  01AUV9WG.jpg   10.0
    6  01B68AKT.jpg    1.0
    7  01DMQGVG.jpg    0.1
    8  01E9AUX7.jpg    0.1
    9  01EAXZMY.jpg    0.2
    **********
    Index(['name', 'label'], dtype='object')
    **********
    name      object
    label    float64
    dtype: object
    **********
    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 39620 entries, 0 to 39619
    Data columns (total 2 columns):
    name     39620 non-null object
    label    39620 non-null float64
    dtypes: float64(1), object(1)
    memory usage: 619.1+ KB
    None
    **********
    count    39620.000000
    mean        19.405411
    std         33.075336
    min          0.100000
    25%          0.500000
    50%          2.000000
    75%         10.000000
    max        100.000000
    Name: label, dtype: float64
  • 面值类别
复制代码
    face_values = np.sort(df_rmb_face_value_labels["label"].unique())
    print(face_values)
复制代码
    [  0.1   0.2   0.5   1.    2.    5.   10.   50.  100. ]
  • 样本分布

探索数据是否平衡。

复制代码
    distribution_rmb_face_value = dict()
    
    for face_value in face_values:
    distribution_rmb_face_value[str(face_value)] = len(
        df_rmb_face_value_labels[df_rmb_face_value_labels["label"] == face_value])
    
    distribution_rmb_face_value = pd.Series(distribution_rmb_face_value)
    
    print(distribution_rmb_face_value)
    distribution_rmb_face_value.plot(kind="bar")
    plt.xticks(rotation=45)
    plt.title("distribution of face values")
    plt.show()
复制代码
1      4233
    0.2      4373
    0.5      4407
    1.0      4424
    2.0      4411
    5.0      4413
    10.0     4283
    50.0     4408
    100.0    4668
    dtype: int64
在这里插入图片描述

1.2 样本

通过统计样本图像长宽比,探索样本中是否存在异常值。

复制代码
    df_samples = pd.DataFrame(columns=["width", "height", "ratio"])
    
    for sample_path in glob.glob(os.path.join(DATASET, TRAIN_IMGS_DIR, "*.jpg")):
    
    str_sample_name = os.path.split(sample_path)[-1]
    # print(sample_path)
    # print(str_sample_name)
    
    img = cv2.imread(sample_path)
    height, width = img.shape[0 : 2]
    
    df_samples.loc[str_sample_name] = [width, height, width / height]
复制代码
    print(df_samples.sample(10))
复制代码
               width  height     ratio
    BCEMGK97.jpg  1139.0   579.0  1.967185
    4WRU1SYD.jpg  1109.0   571.0  1.942207
    V8S7YE61.jpg  1168.0   534.0  2.187266
    E63QHM07.jpg   750.0   353.0  2.124646
    5Z8OQ73V.jpg  1226.0   600.0  2.043333
    CELA9UQV.jpg  1304.0   600.0  2.173333
    TEX65YQR.jpg   825.0   394.0  2.093909
    WYGL2EFU.jpg  1164.0   600.0  1.940000
    3KTP4EOL.jpg  1183.0   600.0  1.971667
    H3QNT5ZS.jpg  1310.0   600.0  2.183333
复制代码
    fig = plt.figure(figsize=(8, 6))
    df_samples.plot(kind="box", ax=fig.add_subplot(111), subplots=True)
    plt.show()
  • 异常值

箱形图查找异常样本

复制代码
    p = df_samples["ratio"].plot(kind="box", return_type='dict')
    plt.show()
    
    outliers = p["fliers"][0].get_ydata()
    outliers.sort()
    print(outliers)
复制代码
    outlier_samples = df_samples[(df_samples["ratio"] > 3) | (df_samples["ratio"] < 1.75)]
    print(outlier_samples.sort_values(by="ratio"))

上述方式能够排查到非货币图像样本及残币图像样本。
在这里插入图片描述

1.3 One-Hot编码

复制代码
    label_encoder = LabelEncoder()
    labels = label_encoder.fit_transform(df_data["label"])
    labels_one_hot = list(to_categorical(labels, num_classes=num_classes))

1.4 切分数据集

复制代码
    X_train, X_val, y_train, y_val = train_test_split(df_data["name"],
                                                   labels_one_hot,
                                                   test_size=VAL_RATIO,
                                                   shuffle=True)
    print("training set X: {}".format(X_train.shape))
    print("training set y: {}".format(np.shape(y_train)))
    print("validation set X: {}".format(X_val.shape))
    print("validation set y: {}".format(np.shape(y_val)))

2 ResNet34面值识别

人民币面值识别为多分类问题,激活函数可选用softmax,网络结构采用ResNet34。

2.1 ResNet34

复制代码
    def Conv2d_BN(x, nb_filter, kernel_size, strides=(1,1), padding="same", name=None):
    
    if name is not None:
        bn_name = name + "_bn"
        conv_name = name + "_conv"
    else:
        bn_name = None
        conv_name = None
    
    x = Conv2D(nb_filter, kernel_size, padding=padding, strides=strides,
               activation="linear", name=conv_name)(x)
    x = BatchNormalization(axis=3, name=bn_name)(x)
    x = Activation(activation="relu")(x)
    
    return x
    
    def Conv_Block(x, nb_filter, kernel_size, strides=(1,1), is_conv_shortcut=False):
    
    shortcut = x
    x = Conv2d_BN(x, nb_filter=nb_filter, kernel_size=kernel_size, strides=strides, padding="same")
    x = Conv2d_BN(x, nb_filter=nb_filter, kernel_size=kernel_size, padding="same")
    
    if is_conv_shortcut:
        shortcut = Conv2d_BN(shortcut, nb_filter=nb_filter, strides=strides, kernel_size=kernel_size)
        
    x = add([x, shortcut])
        
    return x
    
    def get_model_resnet34(input_shape, num_classes):
    
    input_layer = Input(shape=input_shape)
    x = ZeroPadding2D((3,3))(input_layer)
    x = Conv2d_BN(x, nb_filter=64, kernel_size=(7,7), strides=(2,2), padding="valid")
    x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding="same")(x)
    
    # (input_shape[0 : 2] / 4, 64)
    x = Conv_Block(x, nb_filter=64, kernel_size=(3,3))
    x = Conv_Block(x, nb_filter=64, kernel_size=(3,3))
    x = Conv_Block(x, nb_filter=64, kernel_size=(3,3))
    
    # (input_shape[0 : 2] / 8, 128)
    x = Conv_Block(x, nb_filter=128, kernel_size=(3,3), strides=(2,2), is_conv_shortcut=True)
    x = Conv_Block(x, nb_filter=128, kernel_size=(3,3))
    x = Conv_Block(x, nb_filter=128,kernel_size=(3,3))
    x = Conv_Block(x, nb_filter=128,kernel_size=(3,3))
    
    # (input_shape[0 : 2] / 16, 128)
    x = Conv_Block(x, nb_filter=256, kernel_size=(3,3), strides=(2,2), is_conv_shortcut=True)
    x = Conv_Block(x, nb_filter=256, kernel_size=(3,3))
    x = Conv_Block(x, nb_filter=256, kernel_size=(3,3))
    x = Conv_Block(x, nb_filter=256, kernel_size=(3,3))
    x = Conv_Block(x, nb_filter=256, kernel_size=(3,3))
    x = Conv_Block(x, nb_filter=256, kernel_size=(3,3))
    
    # (input_shape[0 : 2] / 32, 128)
    x = Conv_Block(x, nb_filter=512, kernel_size=(3,3), strides=(2,2), is_conv_shortcut=True)
    x = Conv_Block(x, nb_filter=512, kernel_size=(3,3))
    x = Conv_Block(x, nb_filter=512, kernel_size=(3,3))
    x = AveragePooling2D(pool_size=(int(input_shape[0] / 64),
                                    int(input_shape[0] / 64)))(x)
    x = Flatten()(x)
    x = Dense(num_classes, activation="softmax")(x)
    
    model = Model(inputs=input_layer, outputs=x)
    adam = Adam(lr=1e-3)
    model.compile(optimizer=adam, loss="categorical_crossentropy", metrics=["accuracy"])
    
    return model

2.2 数据增强

  • 样本中货币图像存在残缺、偏转、180度翻转,需要数据增强。
复制代码
    image_data_generator = ImageDataGenerator(
    # samplewise_center=True, samplewise_std_normalization=True,
    brightness_range=[0.2, 1.5],
    zoom_range=0.1,
    width_shift_range=0.1, height_shift_range=0.1,
    horizontal_flip=False, vertical_flip=False)
复制代码
    img = cv2.flip(img, -1)
  • 消除亮度影响,直方图均衡
复制代码
    hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
    hsv[:, :, 2] = cv2.equalizeHist(hsv[:, :, 2].astype(np.uint8))
    img = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)

2.3 训练

  • 监控验证准确率、保存最优模型
复制代码
    best_save = ModelCheckpoint(filepath="{}".format(best_model_path),
                            monitor="val_acc",
                            verbose=1,
                            mode="max",
                            save_best_only=True,
                            save_weights_only=False,
                            period=1)
  • 验证准确率未能提升,提前结束训练
复制代码
    early_stop = EarlyStopping(monitor="val_acc",
                           min_delta=0,
                           patience=10,
                           verbose=1,
                           mode="auto")
  • 验证准确率未能提升,减小步长
复制代码
    reduce_learning_rate = ReduceLROnPlateau(monitor="val_acc",
                                         factor=0.2,
                                         patience=5,
                                         epsilon=0.001,
                                         cooldown=0,
                                         min_lr=1e-5)
复制代码
    if __name__ == "__main__":
    
    # 数据读取
    data_path = os.path.join(DATASET_DIR, TRAIN_LABELS_FILE)
    df_data = pd.read_csv(data_path, index_col=None, header=0, delimiter=", ")
    
    num_classes = len(df_data["label"].unique())
    
    label_encoder_path = "./label_encoder.pickle"
    # 标签转换
    if os.path.exists(label_encoder_path):
        with open(label_encoder_path, "rb") as file_pi:
            label_encoder = pickle.load(file_pi)
    else:
        label_encoder = LabelEncoder()
        label_encoder.fit(df_data["label"])
        with open(label_encoder_path, "wb") as file_pi:
            pickle.dump(label_encoder, file_pi)
    
    labels = label_encoder.transform(df_data["label"])
    labels_one_hot = list(to_categorical(labels, num_classes=num_classes))
    
    # 切分数据集
    X_train, X_val, y_train, y_val = train_test_split(df_data["name"],
                                                      labels_one_hot,
                                                      test_size=VAL_RATIO,
                                                      shuffle=True)
    train_dataset_size = len(y_train)
    val_dataset_size = len(y_val)
    steps_per_epoch = np.ceil(train_dataset_size / BATCH_SIZE_TRAIN).astype(np.int)
    validation_steps = np.ceil(val_dataset_size / BATCH_SIZE_VAL).astype(np.int)
    
    # 数据增强器
    image_data_generator = get_image_generator()
    
    best_model_path = os.path.join(MODEL_DIR, "{}_best_model.h5".format(MODEL_NAME))
    model_path = os.path.join(MODEL_DIR, "{}_model.h5".format(MODEL_NAME))
    history_path = os.path.join(MODEL_DIR, "{}_train_history_dict.p".format(MODEL_NAME))
    logger_path = os.path.join(MODEL_DIR, "{}_train_log.p".format(MODEL_NAME))
    
    K.clear_session()
    
    if os.path.exists(best_model_path):
        model = load_model(best_model_path)
    elif os.path.exists(model_path):
        model = load_model(model_path)
    else:
        model = get_model(MODEL_NAME)(INPUT_SHAPE, num_classes)
        
    history = train(model, image_data_generator,
                    X_train, y_train,
                    X_val, y_val,
                    batch_size_train=BATCH_SIZE_TRAIN,
                    batch_size_val=BATCH_SIZE_VAL,
                    steps_per_epoch=steps_per_epoch,
                    validation_steps=validation_steps,
                    best_model_path=best_model_path,
                    model_path=model_path,
                    history_path=history_path,
                    logger_path=logger_path,
                    # is_augmentation=True)
                    is_augmentation=False)
    
    # list all data in histroy
    print(history.history.keys())
    
    # summarize history for accuracy
    fig = plt.figure(figsize=(10, 6))
    fig.suptitle("model accuracy")
    ax = fig.add_subplot(121)
    ax.plot(history.history.get("acc"), label="train")
    ax.plot(history.history.get("val_acc"), label="validation")
    ax.set_xlabel("epoch")
    ax.set_ylabel("accuracy")
    ax.legend(loc="lower right")
    ax = fig.add_subplot(122)
    ax.semilogy(1 - np.array(history.history.get("acc")), label="train")
    ax.semilogy(1 - np.array(history.history.get("val_acc")), label="validation")
    ax.set_xlabel("epoch")
    ax.set_ylabel("1 - accuracy")
    ax.legend(loc="upper right")
    plt.show()
    fig.savefig(os.path.join(MODEL_DIR, "{}_acc.png".format(MODEL_NAME)))
在这里插入图片描述

3 预测

  • 消除亮度影响,直方图均衡
复制代码
    hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
    hsv[:, :, 2] = cv2.equalizeHist(hsv[:, :, 2].astype(np.uint8))
    img = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
  • 返回Top1
复制代码
    np.argmax(pred_vector, axis=1)
  • 预测
复制代码
    y = model.predict(x)
    y = reverse_categorical_top1(y)
    y = label_encoder.inverse_transform(y)
  • 修正非货币图像标签(修正前模型准确率为99.98%,4张非货币图像预测错误;修正后准确率为100%)
复制代码
    def correct_outliers(outliers, predictions):
    
    pd_predictions = pd.DataFrame(predictions, columns=["name", "label"])
    # pd_predictions.set_index("name", inplace=True)
    
    for item in outliers:
        pd_predictions.loc[pd_predictions.loc[:, "name"] == item, "label"] = 0.2
        
    predictions = pd_predictions.values.tolist()
    
    return predictions
  • 写入csv
复制代码
    def predictions_output_csv(filepath, predictions):
    
    with open(filepath, "w") as fw:
        
        fw.write("name, label\n")
        
        for pred in predictions:
            
            if pred[1] >= 1:
                pred[1] = int(pred[1])
                
            fw.write("{0[0]},  {0[1]}\n".format(pred))
            
    return None

全部评论 (0)

还没有任何评论哟~