2020全国大学生数学建模C题初尝试——基于 PaddlePaddle LSTM 的中小微企业信贷决策模型

2020全国大学生数学建模C题初尝试——基于 PaddlePaddle LSTM 的中小微企业信贷决策模型

题目简析

C题题目

首先让我们来回顾一下题目:

在实际中,由于中小微企业规模相对较小,也缺少抵押资产,因此银行通常是依据信贷政策、企业的交易票据信息和上下游企业的影响力,向实力强、供求关系稳定的企业提供贷款,并可以对信誉高、信贷风险小的企业给予利率优惠。银行首先根据中小微企业的实力、信誉对其信贷风险做出评估,然后依据信贷风险等因素来确定是否放贷及贷款额度、利率和期限等信贷策略。

某银行对确定要放贷企业的贷款额度为 10 至 100 万元;年利率为 4% 至 15%;贷款期限为 1 年。附件1~3 分别给出了123家有信贷记录企业的相关数据、302 家无信贷记录企业的相关数据和贷款利率与客户流失率关系的 2019 年统计数据。该银行请你们团队根据实际和附件中的数据信息,通过建立数学模型研究对中小微企业的信贷策略,主要解决下列问题:

(1) 对附件 1 中 123 家企业的信贷风险进行量化分析,给出该银行在年度信贷总额固定时对这些企业的信贷策略。

(2) 在问题 1 的基础上,对附件 2 中 302 家企业的信贷风险进行量化分析,并给出该银行在年度信贷总额为1亿元时对这些企业的信贷策略。

(3) 企业的生产经营和经济效益可能会受到一些突发因素影响,而且突发因素往往对不同行业、不同类别的企业会有不同的影响。综合考虑附件 2 中各企业的信贷风险和可能的突发因素(例如:新冠病毒疫情)对各企业的影响,给出该银行在年度信贷总额为 1 亿元时的信贷调整策略。

简单解析

先让我们看看附件 1 的数据

在这里插入图片描述
其中发票号码,开票具体日期,销方单位代号属于无用信号。同时信号本身属于时序信号,故需要选用适合处理时序信息的神经网络。由于 RNN 在处理长依赖问题具有较为严重的局限性,故最终决定选用 LSTM。

简单说说 LSTM

LSTM 的 全称为 Long Short Term Memory networks,即长短期记忆网络。LSTM 是一种特殊的循环神经网络(Recurrent Neural Networks),该网络设计出来是为了解决 RNN 未能解决的长依赖问题。

所有循环神经网络都具有神经网络的重复模块链的形式。 在标准的 RNN 中,该重复模块将具有非常简单的结构,例如单个 tanh 层。标准的 RNN 网络如下图所示

在这里插入图片描述
LSTM 也具有这种链式结构,但是它的重复单元不同于标准 RNN 网络里的单元只有一个网络层,它的内部有四个网络层,同时具有遗忘门、输入门和输出门等几个部分。LSTM 的结构如下图所示

在这里插入图片描述
其中

在这里插入图片描述

项目代码

笔者使用百度的 AIStudio 线上平台进行模型的训练,环境配置为 Python 2.7 与 PaddlePaddle 1.6.2

数据预处理

由于题目数据量较为庞大,数据预处理部分的工作比较繁杂,处理好的数据如下所示
在这里插入图片描述
从左到右分别为金额,税额,价税合计与发票状态(0 为有效发票,1 为作废发票),标签为公司评级ABCD,分别用1234表示

不想自己处理的小伙伴可以直接白嫖已经处理好的训练数据,提取码如下

链接:https://pan.baidu.com/s/1RLhF5N3EW_xkiO7QZvNH4A
提取码:fit5

数据的导入与训练

引用库文件

import numpy as np
import math
import matplotlib.pyplot as plt
import paddle
import paddle.fluid as fluid 
from __future__ import print_function

文件读入

SAVE_DIRNAME = 'model'
f = open('work/data.txt') #修改数据集文件路径
df = f.readlines()    
f.close()
data = []
for line in df:
    data_raw = line.strip('\n').strip('\r').split('\t') 
    data.append(data_raw)
data = np.array(data, dtype='float32')

打印出来查看一下

print('数据类型:',type(data))
print('数据个数:', data.shape[0])
print('数据形状:', data.shape)
print('数据第一行:', data[0])

在这里插入图片描述

划分训练集和验证集,由于数据量较大,故将训练集的比例划分到 99%,剩余两千左右的数据作为验证

ratio = 0.99
DATA_NUM = len(data)

train_len = int(DATA_NUM * ratio)
test_len = DATA_NUM - train_len

train_data = data[:train_len]
test_data = data[train_len:]

数据归一化,由于标签值的解算较为特殊,故对归一化公式做出一些改动

def normalization(data):
    avg = np.mean(data, axis=0)
    max_ = np.max(data, axis=0)
    min_ = np.min(data, axis=0)
    #result_data = (data - avg) / (max_- min_)
    result_data = data / (max_- min_)
    print(len(result_data))
    print(result_data[:][4])
    return result_data
train_data = normalization(train_data)
test_data = normalization(test_data)

构造 paddlepaddle 的 reader

def my_train_reader():
    def reader():
        for temp in train_data:
            yield temp[:-1], temp[-1]
    return reader
    
def my_test_reader():
    def reader():
        for temp in test_data:
            yield temp[:-1], temp[-1]    
    return reader

定义 batch size 大小

# 定义batch
train_reader = paddle.batch(
    my_train_reader(),
    batch_size=30000)

搭建LSTM模型

DIM = 1
hid_dim2 = 1
x = fluid.layers.data(name='x', shape=[DIM], dtype='float32', lod_level=1)  
label = fluid.layers.data(name='y', shape=[1], dtype='float32')
fc0 = fluid.layers.fc(input=x, size=DIM * 4)
fc1 = fluid.layers.fc(input=fc0, size=DIM * 4)
lstm_h, c = fluid.layers.dynamic_lstm(
    input=fc1, size=DIM * 4, is_reverse=False)

# 最大池化
lstm_max = fluid.layers.sequence_pool(input=lstm_h, pool_type='max')
# 激活函数
lstm_max_tanh = fluid.layers.tanh(lstm_max)
# 全连接层
prediction = fluid.layers.fc(input=lstm_max_tanh, size=hid_dim2, act='tanh')
# 代价函数
cost = fluid.layers.square_error_cost(input=prediction, label=label)
avg_cost = fluid.layers.mean(x=cost)
# acc = fluid.layers.accuracy(input=prediction, label=label)

开始训练,PASS_NUM 为轮数,这里设置为两百

from paddle.utils.plot import Ploter
train_title = "Train cost"
test_title = "Test cost"
plot_cost = Ploter(train_title, test_title)

# 定义优化器
adam_optimizer = fluid.optimizer.Adam(learning_rate=0.002)
adam_optimizer.minimize(avg_cost)

# 使用CPU
#place = fluid.CPUPlace()
# 使用CUDA
place = fluid.CUDAPlace(0)
exe = fluid.Executor(place)
exe.run( fluid.default_startup_program() )
feeder = fluid.DataFeeder(place=place, feed_list=[x, label])

def train_loop():
    step = 0 # 画图
    PASS_NUM = 200
    for pass_id in range(PASS_NUM):
        total_loss_pass = 0#初始化每一个epoch的损失值初始值为0
        for data in train_reader(): #data表示batch大小的数据样本          
            avg_loss_value, = exe.run(
                fluid.default_main_program(), 
                feed= feeder.feed(data), 
                fetch_list=[avg_cost])
            total_loss_pass += avg_loss_value
        # 画图
        plot_cost.append(train_title, step, avg_loss_value)
        step += 1
        plot_cost.plot()
    fluid.io.save_inference_model(SAVE_DIRNAME, ['x'], [prediction], exe)
train_loop()

训练误差如图

在这里插入图片描述

模型训练效果验证

def convert2LODTensor(temp_arr, len_list):
    temp_arr = np.array(temp_arr) 
    temp_arr = temp_arr.flatten().reshape((-1, 1))
    print(temp_arr.shape)
    return fluid.create_lod_tensor(
        data=temp_arr,
        recursive_seq_lens =[len_list],
        place=fluid.CPUPlace()
        )
    

def get_tensor_label(mini_batch):  
    tensor = None
    labels = []
    
    temp_arr = []
    len_list = []
    for _ in mini_batch:   
        labels.append(_[1]) 
        temp_arr.append(_[0]) 
        len_list.append(len(_[0])) 
    tensor = convert2LODTensor(temp_arr, len_list)    
    return tensor, labels

my_tensor = None
labels = None

# 定义batch
test_reader = paddle.batch(
    my_test_reader(),
    batch_size=400000)


for mini_batch in test_reader():
    my_tensor,labels = get_tensor_label(mini_batch)
 
# 选择CPU或者CUDA进行验证   
#place = fluid.CPUPlace() 
place = fluid.CUDAPlace(0)

exe = fluid.Executor(place)
inference_scope = fluid.core.Scope()
with fluid.scope_guard(inference_scope):
    [inference_program, feed_target_names, fetch_targets] = (
        fluid.io.load_inference_model(SAVE_DIRNAME, exe))
    results = exe.run(inference_program,
                      feed= {
    
    'x': my_tensor}, 
                      fetch_list=fetch_targets)

最后绘制图像,由于标签之前被归一化了,这里我们需要对其进行解算

label = label*3 # 标签解算
result_print = results[0].flatten()
result_print = 3*result_print # 预测结果解算
plt.figure()
plt.plot(list(range(len(labels))), labels, color='b')  #蓝线为真实值
plt.plot(list(range(len(result_print))), result_print, color='r')  #红线为预测值
plt.show()

验证结果如下,其中蓝色为真实评级,红色为模型的预测评级

在这里插入图片描述
误差不是很大,效果感觉还可以

未标注公司的评估

由于数据量实在是太大了,导致图片有些难以辨认

在这里插入图片描述

写在最后

本人以后会发布一些关于机器学习模型算法,自动控制算法的其他文章,也会聊一聊自己做的一些小项目,希望读者朋友们能够喜欢。

猜你喜欢

转载自blog.csdn.net/wzduang/article/details/108548673