本章学习目标
- 理解应用: 认识到图像与声音数据在现代金融市场中的巨大潜力。
- 掌握工具: 学习卷积神经网络(CNN)的核心原理,用以从图片中提取价值。
- 拓宽视野: 了解音频信息在金融分析中的独特作用与处理方法。
- 实践技能: 通过完整的Python代码实例,掌握预训练模型在图像识别任务中的应用。
追寻阿尔法:信息优势的探求
在金融市场,我们的核心目标是获得超越市场平均回报的超额收益,即“阿尔法”(\(\alpha\))。
而阿尔法的源泉,正是信息优势 (Informational Edge)。
传统信息的困境
我们追求信息优势,但传统信息源的价值正在被快速稀释。
- 私有信息 (Private Information)
- 效果最好,但往往涉及内幕交易,存在严重的法律与合规风险。
- 公开信息 (Public Information)
- 例如公司财报、宏观经济数据。
- 根据有效市场假说,其价值通常已迅速反映在资产价格中。
那么,新的优势从何而来?
另类数据:信息优势的新来源
答案在于:对公开数据进行更精细、更独特的处理。
这就是另类数据 (Alternative Data) 的核心价值。我们并非去获取秘密信息,而是用更强大的工具,从海量的、公开的、非传统的数据源中,提炼出独到的洞见。
文字之外:一个充满信息的世界
虽然文本分析至关重要,但信息传播远不止于文字。这些视觉与听觉数据,为我们提供了更直观、更及时的洞察。
图像数据
- 卫星与无人机影像
- 预测零售商业绩(停车场车辆)
- 监控工业产出(工厂烟雾)
- 判断原油供需(油轮动向)
声音数据
- 高管采访与业绩会议
- 分析情绪(自信/紧张)
- 检测欺诈信号(声音微特征)
- 快速获取关键信息(语音转文字)
机器学习:解锁另类数据的钥匙
这些图像、声音数据是非结构化的,传统计量经济学方法难以处理。
深度学习模型,特别是我们今天要学习的卷积神经网络 (CNN),是解锁这些数据价值的关键。它们能自动从原始数据中学习和提取有意义的模式。
图像中蕴含的经济信号
一张图片胜过千言万语,在金融领域尤其如此。
案例:利用卫星图像预测零售商业绩
- 数据采集: 对冲基金获取目标零售商(如沃尔玛)停车场的高频卫星图像。
- 模型分析: 使用图像识别模型自动计数停车场内的汽车数量。
- 信号提取: 汽车数量的变化趋势,可以作为预测该公司季度销售额和客流量的高频领先指标。
- 交易决策: 在官方财报发布前,根据该信号调整投资组合,从而获得信息优势。
另一个经典应用:库存清点
- 验证数据: 租用飞机或卫星拍摄零售商仓库,快速验证官方库存数据的准确性,抢先判断其销售状况。
- 自动盘点: 训练机器学习模型对库存商品进行自动识别和计数,实现大规模、高效率的库存监控。
本章案例:识别货架上的电器
我们的代码实践任务是:让计算机自动识别出照片中的电视、微波炉和烤箱。
import matplotlib.pyplot as plt
from PIL import Image
import requests
from io import BytesIO
# 使用一个稳定的URL来加载图片,确保代码可复现
try:
url = 'https://upload.wikimedia.org/wikipedia/commons/3/33/Dacor-Distinctive-Appliances-display.jpg'
response = requests.get(url, timeout=10)
img_pil = Image.open(BytesIO(response.content))
plt.imshow(img_pil)
plt.axis('off')
plt.show()
except Exception as e:
fig, ax = plt.subplots()
ax.text(0.5, 0.5, '图像加载失败\n请检查网络连接',
horizontalalignment='center', verticalalignment='center',
fontsize=12, color='red')
ax.set_xticks([])
ax.set_yticks([])
plt.show()
核心工具:卷积神经网络 (CNN)
要让机器理解图片,我们主要使用卷积神经网络 (Convolutional Neural Network, CNN)。
在深入其内部结构之前,我们先建立一个直观的认识:计算机如何“看待”图像?
计算机眼中的图像:像素与通道
- 像素 (Pixel): 图像的基本单位,每个像素代表图像中一个点。
- 通道 (Channel):
- 灰度图: 只有一个通道,每个像素值代表亮度 (0=黑, 255=白)。
- 彩色图 (RGB): 有三个通道(红、绿、蓝),每个像素由三个数值组成,代表三种颜色的强度。
图像的矩阵表示
对于计算机,一张灰度图就是一个由数字组成的矩阵。每个数字代表一个像素的亮度。
例如,我们可以用 5x5
的矩阵,清晰地表示字母 ‘O’ 和 ‘X’。
CNN 的三大核心构建模块
最简单的CNN模型,可以看作是由三个核心部件按顺序组装起来的“信息处理流水线”。
模块一:卷积层 (Convolutional Layer)
- 目标:从原始图片中检测出基础的特征 (Features)。
- 什么是特征? 图像的局部模式,例如边缘、角落、纹理、颜色块等。
卷积层的工具:卷积核 (Kernel)
卷积核 (或称滤波器 Filter) 是一个很小的矩阵 (例如 3x3
),它被设计用来识别一种特定的微观模式。
- 卷积层的工作,就是让这个小的卷积核,在大的输入图像上滑动 (Slide),并进行计算。
- 它的参数(矩阵中的数值)通过模型训练不断学习和优化,最终变得能够识别有意义的特征。
图解卷积运算
下图展示了一个 5x5
输入图像和一个 2x2
卷积核。卷积核从左上角开始,在输入图像上滑动。
卷积运算步骤
在每个位置,执行元素级相乘 (element-wise product),然后将所有结果求和。
位置 1 (左上角): (0×1) + (1×0) + (1×0) + (0×1) = 0
滑动与重复
卷积核向右滑动一个单位 (步长 Stride = 1),覆盖新的区域,并重复运算。
位置 2: (1×1) + (1×0) + (0×0) + (0×1) = 1
卷积层的产物:特征图 (Feature Map)
当卷积核遍历完整个输入图像后,会得到一个新的、尺寸稍小的矩阵,这就是特征图 (Feature Map)。
- 特征图是原始图像的“浓缩摘要”。
- 它的每个像素值,都代表原始图像对应区域与该卷积核所寻找的特定模式的匹配程度。值越大,匹配度越高。
一个卷积层,多个卷积核
一个卷积核只能检测一种特征。为了识别多种特征(横线、竖线、曲线等),一个卷积层通常包含多个不同的卷积核。
- 每一个卷积核都会生成一个自己独立的特征图。
- 因此,一个卷积层的输出是一叠(多个)特征图,共同描绘了原始图像的多种基础特征。
重要细节:激活函数 (Activation)
在生成特征图后,通常会使用一个激活函数,最常用的是 ReLU (Rectified Linear Unit)。
\[ \large{ReLU(x) = \max(0, x)} \]
- 作用: 它会把特征图里所有的负数值都变为0,保留正数值。
- 目的: 为模型引入非线性 (non-linearity),使得神经网络能够学习更复杂的模式。可以简单理解为“只关注那些显著匹配的特征,忽略不匹配的”。
可选细节:填充(Padding)与步长(Stride)
- 填充 (Padding)
- 在输入图像的边缘添加额外的像素(通常是0)。
- 目的: 保持输出特征图的尺寸与输入图像相同;更好地处理图像边缘的特征。
- 步长 (Stride)
- 卷积核每次滑动的像素数。
- 目的: 步长大于1可以减小输出特征图的尺寸,实现降采样。
模块二:池化层 (Pooling Layer)
- 目标: 对特征图进行降采样 (Downsampling)。
- 两大目的:
- 减少计算量: 特征图尺寸变小,后续计算更快。
- 特征不变性 (Invariance): 让模型对特征在图像中的微小位置变化不那么敏感,从而提高模型的稳健性。
特征不变性直观理解
无论猫在图片的左边还是右边,它仍然是一只猫。池化操作帮助模型忽略这种微小的空间位置差异,更专注于特征本身是否存在。
池化的操作过程
与卷积类似,池化窗口 (通常2x2
) 在特征图上滑动,但没有重叠。它对每个窗口内的数值进行聚合。
池化的两种主要方法
- 最大池化 (Max Pooling): 取池化窗口内所有元素的最大值。最常用。
- 平均池化 (Average Pooling): 取池化窗口内所有元素的平均值。
直观理解: 最大池化可以看作是“只保留每个小区域内最显著的特征信号”。
最大池化实例
对之前的特征图采用最大池化: max(0,1,1,0) = 1
max(1,2,0,1) = 2
max(1,0,2,1) = 2
max(0,1,1,0) = 1
最终得到一个 2x2
的池化后矩阵:
尺寸从 4x4
降为 2x2
,计算量减少了75%!
模块三:全连接层 (Fully Connected Layer)
目标: 在经过多轮“卷积-池化”提取出高级特征后,做出最终的分类预测。
- 输入: 最后的池化层输出被“压平”(Flatten) 成一个长向量。
- 结构: 本质上就是一个标准的前馈神经网络。
- 输出: 对于分类任务,它会输出一个概率分布,表明输入图像属于每个类别的概率。
CNN 的完整训练流程
训练过程是一个不断迭代优化的循环。
真实世界的复杂模型:AlexNet
实际应用的CNN模型会堆叠多个卷积层和池化层,以学习从低级到高级的层级化特征。
- AlexNet (2012): 深度学习领域的里程碑,证明了深层CNN在复杂图像识别任务上的巨大潜力。
挑战:识别图像中的多个物体
在金融应用中,我们往往需要识别和计数多个物体。例如,清点整个货架的库存,而不是为每件商品单独拍照。
这需要更高级的模型,能够同时完成物体定位 (Localization) 和 分类 (Classification)。
高级模型:R-CNN vs. YOLO
R-CNN (Region-based CNN)
两步法: 1. 区域提议: 先用算法找出可能包含物体的候选区域。 2. 分类: 再对每个区域运行CNN进行分类。
缺点: 速度较慢。
YOLO (You Only Look Once)
一步法: 1. 将物体定位和识别合并到一个单一的神经网络中完成。
优点: 速度极快,适合实时应用。
第二部分:代码实践——用PyTorch进行图像识别
实践目标与策略
- 目标: 自动识别 Figure 1 中的电器。
- 策略: 我们将使用一个在大型图像数据集 (COCO) 上预训练 (pre-trained) 好的
Faster R-CNN
模型。
什么是预训练模型?
预训练模型是在海量数据集(如包含数百万张图片的ImageNet或COCO)上已经训练好的模型。
为什么要使用它?
- 节省时间与算力: 从零开始训练一个深度模型需要数天甚至数周,以及昂贵的硬件。
- 性能更优: 模型已经从海量数据中学到了通用的图像特征(边缘、纹理等),我们只需在其基础上进行微调或直接使用,就能在自己的任务上获得很好效果。
我们的代码流程
- 环境准备: 导入必要的 Python 库。
- 数据加载: 从网络URL下载并准备图像。
- 模型加载: 加载 PyTorch 中预训练好的 Faster R-CNN 模型。
- 数据预处理: 将图像转换为模型所需的张量 (Tensor) 格式。
- 执行预测: 将张量输入模型,获取预测结果。
- 结果可视化: 将模型识别出的边界框和标签绘制在原始图像上。
步骤 1:导入必要的 Python 库
#| label: code-imports
#| code-line-numbers: true
# PyTorch核心库和图像处理工具
import torch
import torchvision.models as models
from torchvision import transforms
# 图像处理和绘图库
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib.patches as patches
# 用于从网络下载图片的库
import requests
from io import BytesIO
步骤 2:下载并准备图像
为了让代码可复现,我们直接从网络URL加载图片,而不是依赖本地文件。
#| label: code-load-image
# 一个公开、稳定的货架图片URL
url = 'https://upload.wikimedia.org/wikipedia/commons/3/33/Dacor-Distinctive-Appliances-display.jpg'
# 使用try-except块来处理可能的网络错误
try:
response = requests.get(url)
response.raise_for_status() # 如果请求失败则抛出异常
img_pil = Image.open(BytesIO(response.content))
# 显示下载的图片
plt.imshow(img_pil)
plt.title("Downloaded Image")
plt.axis('off')
plt.show()
except requests.exceptions.RequestException as e:
print(f"图像下载失败: {e}")
步骤 3:加载预训练的 Faster R-CNN 模型
pretrained=True
是关键,它告诉 PyTorch 加载已经在大规模数据集上训练好的权重。
#| label: code-load-model
# 加载基于 ResNet50 骨干网络的 Faster R-CNN 模型
# pretrained=True 会自动下载并加载预训练权重
model = models.detection.fasterrcnn_resnet50_fpn(pretrained=True)
# 将模型设置为评估模式 (evaluation mode)
# 这会关闭 Dropout 和 BatchNorm 等只在训练时使用的层,确保预测结果的确定性
model.eval()
步骤 4:定义图像预处理操作
模型需要的输入不是原始的PIL图像,而是特定格式的张量 (Tensor)。
#| label: code-transform
# 定义一个转换流程:将PIL图像转换为PyTorch张量
transform = transforms.Compose([
transforms.ToTensor(),
])
# 应用转换
img_tensor = transform(img_pil)
# 打印张量的形状,理解其结构
# 输出应为 [通道数, 高度, 宽度],例如
print(f"图像张量的形状: {img_tensor.shape}")
transforms.ToTensor()
会将图像的像素值从 [0, 255]
范围归一化到 [0.0, 1.0]
,并调整维度顺序。
步骤 5:准备模型输入并进行预测
模型期望的输入是一个 batch
(一批)图像,所以我们需要为单个图像增加一个批次维度。
#| label: code-predict
# 使用 unsqueeze(0) 在最前面增加一个维度: [C, H, W] -> [1, C, H, W]
input_batch = img_tensor.unsqueeze(0)
# 使用 torch.no_grad() 来关闭梯度计算,这在推理(预测)时可以节省内存和计算资源
with torch.no_grad():
prediction = model(input_batch) # 直接获取第一个(也是唯一一个)结果
# prediction 是一个字典,包含了检测结果
# print(prediction.keys()) # dict_keys(['boxes', 'labels', 'scores'])
步骤 6:定义类别名称
模型的输出标签是数字编码。我们需要一个字典来将这些数字映射回人类可读的物体名称。我们只定义我们关心的几个类别。
#| label: code-coco-map
# COCO 数据集的部分类别名称
# 模型的输出是一个数字,我们用这个字典来查找对应的名称
COCO_INSTANCE_CATEGORY_NAMES = {
72: 'tv', 73: 'laptop', 74: 'mouse', 75: 'remote', 76: 'keyboard',
77: 'cell phone', 78: 'microwave', 79: 'oven', 80: 'toaster',
81: 'sink', 82: 'refrigerator'
}
步骤 7:编写可视化函数
我们编写一个函数,将预测结果(边界框、标签、置信度分数)绘制到原始图像上。
#| label: code-show-detections-def
#| code-line-numbers: true
def show_detections(image, prediction, threshold=0.5):
fig, ax = plt.subplots(1, figsize=(12, 9))
ax.imshow(image)
boxes = prediction['boxes']
labels = prediction['labels']
scores = prediction['scores']
for i in range(len(boxes)):
score = scores[i]
# 只显示置信度高于阈值的检测结果
if score > threshold:
box = boxes[i].cpu().numpy()
label_id = labels[i].item()
label_text = COCO_INSTANCE_CATEGORY_NAMES.get(label_id, f'Label {label_id}')
x1, y1, x2, y2 = box
# 创建一个矩形框
rect = patches.Rectangle((x1, y1), x2 - x1, y2 - y1,
linewidth=2, edgecolor='lime', facecolor='none')
ax.add_patch(rect)
# 在矩形框左上角添加标签和分数
ax.text(x1, y1 - 5, f'{label_text} {score:.2f}',
bbox=dict(facecolor='yellow', alpha=0.5), fontsize=10, color='black')
plt.axis('off')
plt.show()
步骤 8:运行并展示最终结果!
现在,调用我们定义的函数,传入原始图片和模型的预测结果。
Code
# Re-run all necessary code for this final chunk to be self-contained
import torchvision.models as models
from torchvision import transforms
import torch
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from PIL import Image
import requests
from io import BytesIO
def show_detections_final(image, prediction_list, threshold=0.5):
fig, ax = plt.subplots(1, figsize=(12, 9))
ax.imshow(image)
coco_names = {72: 'tv', 73: 'laptop', 74: 'mouse', 75: 'remote', 76: 'keyboard', 77: 'cell phone', 78: 'microwave', 79: 'oven', 80: 'toaster', 81: 'sink', 82: 'refrigerator'}
prediction = prediction_list # The model returns a list with one dict
if not prediction or 'boxes' not in prediction: return
boxes, labels, scores = prediction['boxes'], prediction['labels'], prediction['scores']
for i in range(len(boxes)):
score = scores[i]
if score > threshold:
box = boxes[i].cpu().numpy()
label_id = labels[i].item()
label_text = coco_names.get(label_id, f'Label {label_id}')
x1, y1, x2, y2 = box
rect = patches.Rectangle((x1, y1), x2 - x1, y2 - y1, linewidth=2, edgecolor='lime', facecolor='none')
ax.add_patch(rect)
ax.text(x1, y1 - 5, f'{label_text} {score:.2f}', bbox=dict(facecolor='yellow', alpha=0.5), fontsize=10, color='black')
plt.axis('off')
plt.show()
# Execute the full pipeline
try:
url = 'https://upload.wikimedia.org/wikipedia/commons/3/33/Dacor-Distinctive-Appliances-display.jpg'
response = requests.get(url, timeout=10)
response.raise_for_status()
img_pil = Image.open(BytesIO(response.content))
model = models.detection.fasterrcnn_resnet50_fpn(pretrained=True)
model.eval()
transform = transforms.Compose([transforms.ToTensor()])
img_tensor = transform(img_pil)
input_batch = img_tensor.unsqueeze(0)
with torch.no_grad():
prediction = model(input_batch)
# Set a high threshold to show only confident predictions
show_detections_final(img_pil, prediction, threshold=0.7)
except Exception as e:
print(f"代码执行出错: {e}")
解读结果与局限
结果解读: 大家看,结果非常惊人。模型不仅准确地识别出了微波炉 (microwave) 和烤箱 (oven),还给出了每个识别结果的置信度分数。
模型的局限: - 类别限制: 模型只能识别出它在训练数据(COCO数据集)中见过的物体。 - 识别错误: 对于不常见的物体或复杂的场景,模型可能会出错或漏检。 - 下一步: 如果需要在特定领域(如识别特定型号的工业零件)获得更高精度,我们可以对预训练模型进行微调 (Fine-tuning)。
传统智慧:技术分析
长久以来,金融从业者都试图通过分析股市回报的图表 (Charts) 来预测未来走势,这被称为技术分析。
- 核心思想: 历史价格的模式会重复出现。
- 经典模式: 例如头肩顶、双底、金叉等。
新兴趋势:让CNN“看懂”股票图表
一个革命性的想法:我们能否将股票价格走势图本身当作一张图片,然后用CNN来分析它,从而预测未来的股票回报?
核心论文: Jiang, Kelly, and Xiu (2023), “(Re-)Imag(in)ing Price Trends”, The Journal of Finance.
J.K.X. (2023) 的核心思想
- 数据转换: 将每只股票过去一段时间(如一年)的价格序列,渲染成一张标准化的价格走势图图片。
- 模型训练:
- 输入 (X): 股票价格图(.png 文件)。
- 标签 (Y): 该股票未来一个月的回报。
- 任务: 训练一个深度CNN模型,学习从“图的模式”到“未来回报”的映射关系。
J.K.X. (2023) 的惊人发现
- 发现新模式
- CNN能够发现许多传统技术分析和量化模型(如动量、反转)无法捕捉到的、预测未来回报的非线性信号。
- 超额收益
- 基于CNN预测信号构建的投资组合,能够获得显著的、在经济上和统计上都非常可观的超额收益。
- 稳健性
- 从美国股市图表中学习到的模式,在国际市场上同样有效,证明了这些模式具有一定的普适性。
- 启示
- 这篇论文雄辩地证明了,将最新的机器学习技术应用于金融数据,可以从看似饱和的信息中挖掘出全新的alpha来源。
声音:另一个价值洼地
除了图像,声音——尤其是人类的语音——也包含了丰富的信息。处理声音数据主要有两条路径。
路径一:语音转文字 (Speech-to-Text)
- 核心任务: 将语音信息转化为文字信息。
- 代表模型: OpenAI 的 Whisper 模型是目前最先进的开源语音识别模型之一。
- 金融应用: 自动转录公司的业绩发布会、分析师电话会议、高管访谈等,将其转化为文本,然后就可以用我们之前学过的NLP技术进行情绪分析、主题建模等。
路径二:分析非语言信息 (Vocal Analysis)
我们说话时,传递的不仅仅是文字内容,还有情绪。
- 核心任务: 从语音的声学特征(如音高、语速、音量、音色)中,直接分析说话者的情绪状态。
- 金融应用: 分析CEO在业绩发布会上的声音情绪。
- 如果CEO在描述公司前景时声音充满自信,这可能是一个积极信号。
- 反之,如果言辞闪烁、语气紧张,则可能预示着潜在的问题。
声音的可视化:声谱图
如何让一个为图像设计的CNN模型来处理声音?
答案是:将一维的声音信号转换为二维的声谱图 (Spectrogram)。
声谱图就像是声音的“指纹”,CNN可以像分析普通图片一样,从声谱图中学习和识别声音模式。
结论:另类数据处理的未来
今天,我们打开了一扇通往新世界的大门。我们学习了:
- 另类数据的价值
- 它是信息严重同质化的现代金融市场中,获取超额收益的关键。
- CNN的强大
- 卷积神经网络是解锁图像数据价值的核心工具,其应用从实体经济监控到纯粹的金融预测。
- 声音信息的潜力
- 无论是转录为文本,还是直接分析情绪,语音数据都为我们理解公司基本面提供了全新的维度。
- 核心竞争力
- 随着算力的提升和模型的发展,从这些非结构化数据中提取价值的能力,将成为未来顶尖金融机构的核心竞争力。
课后思考与习题
- 头脑风暴: 除了本章提到的停车场汽车和库存盘点,还有哪些语音和图片数据,可能有助于投资者预测公司的盈利能力?(例如:工厂的开工迹象、社交媒体上的产品图片、建筑工地的活跃度等)
- 概念理解: 在我们的代码实践中,模型需要从图片中提炼出哪些信息才能完成识别任务?(提示:物体的轮廓、颜色、纹理、相对位置等)
- 动手实践: 请在你附近的超市或商店用手机拍一张照片,尝试修改并运行本章中的程序(可能需要查找COCO数据集中对应的类别ID),看看模型能否识别出你照片中的商品。