首先让我们来回顾一下题目:
在实际中,由于中小微企业规模相对较小,也缺少抵押资产,因此银行通常是依据信贷政策、企业的交易票据信息和上下游企业的影响力,向实力强、供求关系稳定的企业提供贷款,并可以对信誉高、信贷风险小的企业给予利率优惠。银行首先根据中小微企业的实力、信誉对其信贷风险做出评估,然后依据信贷风险等因素来确定是否放贷及贷款额度、利率和期限等信贷策略。
某银行对确定要放贷企业的贷款额度为 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 的 全称为 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()
验证结果如下,其中蓝色为真实评级,红色为模型的预测评级
由于数据量实在是太大了,导致图片有些难以辨认
本人以后会发布一些关于机器学习模型算法,自动控制算法的其他文章,也会聊一聊自己做的一些小项目,希望读者朋友们能够喜欢。