ahocorasick:构建短信特征的利器

· 2026-01-01 14:49 · 7 阅读

原创 古明地觉 2026-01-01 14:49 北京

在信贷风控领域,短信特征(即从借款人授权的短信内容中提取的结构化信息)正扮演着越来越重要的角色。它的核心价值在于,为金融机构提供了一个独特且有效的窗口,以评估那些传统征信记录覆盖不足或缺失的客群的信用风险。这对于拓展普惠金融服务、提升风控模型的精准度和覆盖面具有重要意义。

短信特征在信贷风控中的主要用途可以归结为以下几个方面:

  • 补充传统征信的不足: 对于缺乏央行征信记录或信贷历史较短的 "信用白户",短信数据成为了评估其还款意愿和还款能力的重要替代数据源,通过分析短信中的消费、缴费、收入等信息,可以初步勾勒出用户的财务状况。

  • 多维度交叉验证,防范欺诈风险: 短信特征可以与用户在申请贷款时提交的其它信息进行交叉验证。例如可以通过分析银行、三方支付等发送的交易短信,核实用户的收入流水、消费水平和负债情况,有效识别信息造假和潜在的欺诈行为。

  • 构建更全面的用户画像: 短信内容涵盖了用户生活的方方面面,通过对这些非结构化数据进行深度挖掘和分析,可以构建出比传统数据更为立体和丰富的用户画像,这有助于更精准地评估用户的信用等级。

风控模型通常会从海量的短信数据中提取出多种维度的特征,这些特征共同构成了对借款人信用的综合评估。那么从短信中可以挖掘出哪些关键特征呢?

特征类别

具体内容

风控应用价值

收入与消费特征

工资入账、交易提醒、账单支付(信用卡、花呗、白条等)、线上线下消费记录。

评估用户的收入稳定性、消费能力和消费习惯,判断其现金流状况。

资产与负债特征

银行存款变动、理财产品通知、贷款审批与还款提醒、信用卡账单及额度信息。

了解用户的资产水平、负债规模和还款压力,评估其整体偿债能力。

行为与习惯特征

水电煤气等公共事业缴费提醒、航旅出行信息、会员服务通知、验证码短信。

分析用户的稳定性(如居住稳定性)、生活规律性以及对不同服务的需求和偏好。

风险预警特征

催收短信、逾期提醒、赌博或博彩类应用的验证码或通知、多头借贷平台的注册和借款信息。

及时发现用户的潜在风险行为,如逾期、多头借贷、不良嗜好等,作为重要的风险预警信号。

而对于出海现金贷,我们通常关注的是最后一个,那么具体都要做哪些工作呢?

1)关键词匹配与规则引擎

首先风控系统会内置一个庞大的、持续更新的关键词库和规则库,这些库包含了市面上几乎所有已知现金贷平台的信息。

然后通过发件人的号码或名称、以及匹配关键词,来识别是否是现金贷平台发送的短信。

2)自然语言处理(NLP)

对于更复杂的短信,简单的关键词匹配可能不够,因为有可能是平台广告,这时就需要引入 NLP 技术来判断短信的真实意图。

3)文本挖掘

从文本中提取关键的结构化信息,比如借款平台名称、申请时间、申请金额、授信额度、放款金额、还款日、是否命中指定的关键词等。

当然啦,如果你知道其它现金贷平台的短信格式,能够区分出广告,那么 NLP 这一步也可以省略。

4)特征工程与指标计算

当短信被识别和解析后,系统会围绕多头借贷和逾期这两个核心风险点,构建一系列量化特征指标。

  • 申请平台数量: 在特定时间窗口内(如过去 7 天、15 天、30 天),用户向多少个不同的贷款平台发起了申请?如果平台过多,说明可能存在资金紧张、有多头借贷风险。

  • 申请频率: 用户在短期内申请贷款的次数。例如一天内向超过 3 个平台发送注册验证码请求,就是一个强烈的风险信号。

  • 命中高风险平台数量: 用户是否在已知的高利贷、套路贷或风控口碑差的平台上进行过申请?

  • 信贷审批通过率: 在所有申请的平台中,有多少比例的申请被批准了?如果申请了很多家但都失败了,说明该用户资质可能存在严重问题。

  • 贷平台数量: 根据放款和还款短信,判断用户当前同时在多少个平台上有尚未结清的贷款。

  • 逾期次数、天数:判断短信是否命中逾期、催收等关键词,获取逾期次数和天数。

基于以上特征便可以构建准入策略了,比如逾期次数大于 3,直接拒绝;使用的现金贷平台数量大于 10,直接拒绝。

所以短信特征工程的本质是通过用户的数字行为轨迹来评估其信用风险和还款能力,这套系统特别聪明的地方在于:

  • 全景视角:不只看一家的贷款记录,而是看整个市场的信贷行为。

  • 动态追踪:通过时序特征捕捉用户信用状况的变化趋势。

  • 细粒度分析:从 APP 级别到具体的金额、期限、逾期天数等。

  • 行为预测:基于历史模式预测未来的还款表现。

核心就是:你的短信暴露了你的真实财务状况。出现逾期,代表还款能力有问题;出现多平台借贷短信,代表资金紧张;出现催收短信,代表信用状况恶化。

所以短信特征本质上就是通过短信文本分析来做信用评估,这比传统的征信报告更加实时和全面,所以它在风控领域中非常重要。


关于短信特征,我们就简单聊了一下,当然接下来才是本文真正的重点。我们说了,要通过解析短信文本来构建一系列特征指标,那么这个解析过程该怎么做呢?

毫无疑问,解析过程的核心是关键词匹配,所以正则就是一个非常自然的选择。但我们不能光依赖正则,因为在匹配具体的关键词时,正则的速度不够快,我们还需要一个模块叫 ahocorasick。

有一个算法叫 Aho-Corasick,也被称为 AC 自动机,该算法是一种高效的字符串匹配算法,它能一次性在一个文本中查找多个模式串(关键词)。该算法的巧妙之处在于,无论你有多少个关键词,都只需要对主文本进行一次遍历即可完成查找,因此效率极高。你可以把它想象成升级版的 KMP 算法,KMP 是为了解决一个模式串的匹配问题,而 Aho-Corasick 则是为了解决多个模式串的匹配问题。

AC 自动机的具体原理这里就不赘述了,只需要知道它很高效,时间复杂度为 O(N+M+K),其中 N 是主文本长度,M 是所有模式串的总长度(用于构建 Trie 树),K 是匹配到的模式串数量。因此可以看出,AC 自动机和模式串的数量无关,即不管 K 是多少,都只需要遍历一次主文本。

Aho-Corasick(AC 自动机)因其高效的多模式匹配特性,广泛应用于以下场景。

  • 敏感词过滤:在论坛、聊天室等场景中,需要从用户输入中快速过滤掉大量的敏感词、广告词,AC 自动机是实现这个功能的理想选择。

  • 网络入侵检测系统(NIDS):通过维护一个包含大量已知攻击模式(如病毒签名、恶意代码片段)的字典,AC 自动机可以实时地在网络流量中检测是否存在这些攻击模式。

  • 生物信息学:在 DNA 或蛋白质序列中快速查找多个特定的基因序列模式。

  • 拼写检查和自动纠错:将正确的单词作为模式串,快速在文本中找到不存在于字典中的词。

  • 多头借贷检测:在用户授权的短信中,将各个借贷平台的名称和对应关键词作为模式串集合,可以快速地一次性扫描所有短信,识别出多头借贷行为。

而 ahocorasick 模块正是 Aho-Corasick 算法的具体实现,执行 pip install pyahocorasick 安装之后,来看一下它的使用方法。

import ahocorasick
A = ahocorasick.Automaton()
# 添加关键词
A.add_word("what"None)
A.add_word("how"None)
A.add_word("when"None)
A.add_word("then"None)
# 查找所有的关键词
print(list(A.keys()))  # ['then', 'how', 'when', 'what']
# 查找以 "w" 开头的关键词
print(list(A.keys("w")))  # ['when', 'what']
# 查找长度为 4 的关键词,
print(list(A.keys("****""*")))  # ['then', 'when', 'what']
# 第二个参数为通配符,一旦指定了,那么第一个参数就不再是前缀了
# 此时 "*e" 表示精确匹配长度为 2、以 "e" 结尾的关键词
print(list(A.keys("*e""*")))  # []
print(list(A.keys("w""*")))  # []
print(list(A.keys("**e""*")))  # []
print(list(A.keys("**en""*")))  # ['then', 'when']
# 判断某个关键词是否存在(只能精确匹配)
print(A.exists("whe"))  # False
print(A.exists("when"))  # True
# 判断是否存在具有指定前缀的关键词
print(A.match("whe"))  # True
print(A.match("when"))  # True
# 获取某个关键词的 value(只能精确匹配)
# 关键词不存在会报 KeyError,但可以指定默认值
print(A.get("when"))  # None
# 这个不好解释,我们直接用代码说明
# "when" 长度为 4,能命中已有关键词
print(A.longest_prefix("when"))  # 4
# "while" 长度为 5,但只有前两个字符能和已有关键词具有相同的前缀
print(A.longest_prefix("while"))  # 2
print(A.longest_prefix("w"))  # 1
# 没有任何一个关键词的前缀是 "ow"
print(A.longest_prefix("ow"))  # 0
# 如果不想要某个关键词了,那么也可以移除
A.pop("when")
print(list(A.keys()))  # ['then', 'how', 'what']
# pop 方法在 key 不存在时会跑抛出 KeyError
# 如果希望不报错,那么可以使用 remove_word 方法,成功移除返回 True,关键词不存在返回 False
A.remove_word("how")
print(list(A.keys()))  # ['then', 'what']
print(list(A.keys()))  # ['then', 'what']
# 清空所有关键词
A.clear()
print(list(A.keys()))  # []

以上这些方法只是开胃菜,因为我们用字典也可以实现,下面来看它最重要的一个用法。

import ahocorasick
A = ahocorasick.Automaton()
# 添加关键词
key_words = ["what""how""when"]
for index, key_word in enumerate(key_words):
# value 可以是任何对象,这里我们用 (index, key_word) 元组
    A.add_word(key_word, (index, key_word))
# 构建自动机,只有在构建完自动机之后,才可以搜索
A.make_automaton()
# 原始文本
original_text = "what are you doing? how do you do? when i was young, i'd listen to the radio."
# 调用 iter 方法进行搜索,该方法会返回一个迭代器,迭代出的每个元素是一个元组 (end_index, value)
# 其中 end_index 表示匹配到的关键词在文本中的结束位置(包含),value 表示添加关键词时指定的 value
# 我们看到没有返回关键词,所以在 add_word 时要将关键词体现在 value 中,当前的 value 是 (index, key_word)
for end_index, (index, key_word) in A.iter(original_text):
    print(f"original_text[{end_index + 1 - len(key_word)}{end_index + 1}] 对应关键词 {key_word},"
f"即 key_words[{index}]")"""
original_text[0: 4] 对应关键词 what,即 key_words[0]
original_text[20: 23] 对应关键词 how,即 key_words[1]
original_text[35: 39] 对应关键词 when,即 key_words[2]
"""
print(original_text[04])  # what
print(original_text[2023])  # how
print(original_text[3539])  # when

通过这种方式,只需一次遍历,就可以找到所有匹配的关键词。当然啦,大部分时候我们不关心匹配的单词在原文中的位置,只是想统计数量。

import ahocorasick
A = ahocorasick.Automaton()
key_words = ["what""how""when"]
for index, key_word in enumerate(key_words):
    A.add_word(key_word, key_word)
A.make_automaton()
original_text = "what how when when how how how what"
result = {}
for _, key_word in A.iter(original_text):
if key_word notin result:
        result[key_word] = 0
    result[key_word] += 1
print(result)  # {'what': 2, 'how': 4, 'when': 2}

需要注意的是,这些关键词在原文中都是分开的,即使它们连在一起也没问题。

original_text = "whathowwhenwhenhowhowhowwhat"
result = {}
for _, key_word in A.iter(original_text):
if key_word notin result:
        result[key_word] = 0
    result[key_word] += 1
print(result)  # {'what': 2, 'how': 4, 'when': 2}

结果是一样的,因为 AC 自动机在遍历时不在乎关键词的右边是否是空格。

另外 AC 自动机在匹配时,如果原始字符串的一个范围能匹配多个单词,那么这些单词都会返回。

import ahocorasick
A = ahocorasick.Automaton()
key_words = ["he""she""his""hers"]
for index, key_word in enumerate(key_words):
    A.add_word(key_word, key_word)
A.make_automaton()
original_text = "ushers"
for end_index, key_word in A.iter(original_text):
    print(f"original_text[{end_index + 1 - len(key_word)}{end_index + 1}] 对应关键词 {key_word}")"""
original_text[1: 4] 对应关键词 she
original_text[2: 4] 对应关键词 he
original_text[2: 6] 对应关键词 hers
"""

以上就是 ahocorasick 的用法,当然它还有一些其它方法,不过不常用。我们使用这个模块,主要就是做关键词匹配。

阅读原文

跳转微信打开