金融时间序列分析:3. First Demo By Python

本文是金融时间序列分析系列的第三篇,主要介绍如何使用Python进行实战。内容包括获取和预处理数据,数据展示,平稳性检验的观察法和ADF检验。文章强调了Python在集成系统中的优势,尽管R在时间序列分析上更直观。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0. 目录

金融时间序列分析:9. ARMA自回归移动平均模型
金融时间序列分析:8. MA模型实例(Python)
金融时间序列分析:7. MA滑动平均模型
金融时间序列分析:6. AR模型实例
金融时间序列分析:5. AR模型实例(Python)
金融时间序列分析:4. AR自回归模型
金融时间序列分析:3. First Demo By Python
金融时间序列分析:2. 数学分析模型
金融时间序列分析:1. 基础知识


1. 前言

金融时间序列分析:1. 基础知识
金融时间序列分析:2. 数学分析模型

前面2篇文章讲了金融时间序列分析的基础知识,本文简单介绍下怎么实战。
网上有很多用R语言进行金融时间序列分析的资料,但是用Python的不多,我在此介绍下怎么用Python操作,至于R语言怎么弄,读者随便在网上查查就好了。

PS: 在时间序列分析领域R比Python简单的多,如果单单是进行分析的话R就够了,但是要集成一个系统的话就得用Python。(个人之见)


2. 基础知识

2.1 基本步骤

  1. 获取被观测系统时间序列数据;
  2. 对数据绘图,观测是否为平稳时间序列;对于非平稳时间序列要先进行d阶差分运算,化为平稳时间序列;
  3. 经过第二步处理,已经得到平稳时间序列。要对平稳时间序列分别求得其自相关系数ACF 和偏自相关系数PACF ,通过对自相关图和偏自相关图的分析,得到最佳的阶层 p 和阶数 q
  4. 由以上得到的d、q、p ,选择合适的模型。然后开始对得到的模型进行模型检验。

本文只讨论前3步,至于后面的再提到具体的模型的时候回在补上。

2.2 Python时间序列分析

用Python进行时间序列分析需要用到下面一些库:
pandas,numpy,scipy,matplotlib,statsmodels

其中一些的基本使用参考下面的文章:
十分钟搞定pandas
Numpy Tutor
Python应用matplotlib绘图简介


3. 获取数据

有两种方式:
(1)从网络获取
这个参考以前的文章:
Python获取Yahoo股票数据

(2)从本地读取
在金融时间序列中CSV文件格式是一种非常简单的文件格式,最为常见的格式。

class KeyWordSpotter(torch.nn.Module): def __init__( self, ckpt_path, config_path, token_path, lexicon_path, threshold, min_frames=5, max_frames=250, interval_frames=50, score_beam=3, path_beam=20, gpu=-1, is_jit_model=False, ): super().__init__() os.environ[&#39;CUDA_VISIBLE_DEVICES&#39;] = str(gpu) with open(config_path, &#39;r&#39;) as fin: configs = yaml.load(fin, Loader=yaml.FullLoader) dataset_conf = configs[&#39;dataset_conf&#39;] # feature related self.sample_rate = 16000 self.wave_remained = np.array([]) self.num_mel_bins = dataset_conf[&#39;feature_extraction_conf&#39;][ &#39;num_mel_bins&#39;] self.frame_length = dataset_conf[&#39;feature_extraction_conf&#39;][ &#39;frame_length&#39;] # in ms self.frame_shift = dataset_conf[&#39;feature_extraction_conf&#39;][ &#39;frame_shift&#39;] # in ms self.downsampling = dataset_conf.get(&#39;frame_skip&#39;, 1) self.resolution = self.frame_shift / 1000 # in second # fsmn splice operation self.context_expansion = dataset_conf.get(&#39;context_expansion&#39;, False) self.left_context = 0 self.right_context = 0 if self.context_expansion: self.left_context = dataset_conf[&#39;context_expansion_conf&#39;][&#39;left&#39;] self.right_context = dataset_conf[&#39;context_expansion_conf&#39;][ &#39;right&#39;] self.feature_remained = None self.feats_ctx_offset = 0 # after downsample, offset exist. # model related if is_jit_model: model = torch.jit.load(ckpt_path) # For script model, only cpu is supported. device = torch.device(&#39;cpu&#39;) else: # Init model from configs model = init_model(configs[&#39;model&#39;]) load_checkpoint(model, ckpt_path) use_cuda = gpu >= 0 and torch.cuda.is_available() device = torch.device(&#39;cuda&#39; if use_cuda else &#39;cpu&#39;) self.device = device self.model = model.to(device) self.model.eval() logging.info(f&#39;model {ckpt_path} loaded.&#39;) self.token_table = read_token(token_path) logging.info(f&#39;tokens {token_path} with &#39; f&#39;{len(self.token_table)} units loaded.&#39;) self.lexicon_table = read_lexicon(lexicon_path) logging.info(f&#39;lexicons {lexicon_path} with &#39; f&#39;{len(self.lexicon_table)} units loaded.&#39;) self.in_cache = torch.zeros(0, 0, 0, dtype=torch.float) # decoding and detection related self.score_beam = score_beam self.path_beam = path_beam self.threshold = threshold self.min_frames = min_frames self.max_frames = max_frames self.interval_frames = interval_frames self.cur_hyps = [(tuple(), (1.0, 0.0, []))] self.hit_score = 1.0 self.hit_keyword = None self.activated = False self.total_frames = 0 # frame offset, for absolute time self.last_active_pos = -1 # the last frame of being activated self.result = {} def set_keywords(self, keywords): # 4. parse keywords tokens assert keywords is not None, \ &#39;at least one keyword is needed, &#39; \ &#39;multiple keywords should be splitted with comma(,)&#39; keywords_str = keywords keywords_list = keywords_str.strip().replace(&#39; &#39;, &#39;&#39;).split(&#39;,&#39;) keywords_token = {} keywords_idxset = {0} keywords_strset = {&#39;<blk>&#39;} keywords_tokenmap = {&#39;<blk>&#39;: 0} for keyword in keywords_list: strs, indexes = query_token_set(keyword, self.token_table, self.lexicon_table) keywords_token[keyword] = {} keywords_token[keyword][&#39;token_id&#39;] = indexes keywords_token[keyword][&#39;token_str&#39;] = &#39;&#39;.join(&#39;%s &#39; % str(i) for i in indexes) [keywords_strset.add(i) for i in strs] [keywords_idxset.add(i) for i in indexes] for txt, idx in zip(strs, indexes): if keywords_tokenmap.get(txt, None) is None: keywords_tokenmap[txt] = idx token_print = &#39;&#39; for txt, idx in keywords_tokenmap.items(): token_print += f&#39;{txt}({idx}) &#39; logging.info(f&#39;Token set is: {token_print}&#39;) self.keywords_idxset = keywords_idxset self.keywords_token = keywords_token def accept_wave(self, wave): assert isinstance(wave, bytes), \ "please make sure the input format is bytes(raw PCM)" # convert bytes into float32 data = [] for i in range(0, len(wave), 2): value = struct.unpack(&#39;<h&#39;, wave[i:i + 2])[0] data.append(value) # here we don&#39;t divide 32768.0, # because kaldi.fbank accept original input wave = np.array(data) wave = np.append(self.wave_remained, wave) if wave.size < (self.frame_length * self.sample_rate / 1000) \ * self.right_context : self.wave_remained = wave return None wave_tensor = torch.from_numpy(wave).float().to(self.device) wave_tensor = wave_tensor.unsqueeze(0) # add a channel dimension feats = kaldi.fbank(wave_tensor, num_mel_bins=self.num_mel_bins, frame_length=self.frame_length, frame_shift=self.frame_shift, dither=0, energy_floor=0.0, sample_frequency=self.sample_rate) # update wave remained feat_len = len(feats) frame_shift = int(self.frame_shift / 1000 * self.sample_rate) self.wave_remained = wave[feat_len * frame_shift:] if self.context_expansion: assert feat_len > self.right_context, \ "make sure each chunk feat length is large than right context." # pad feats with remained feature from last chunk if self.feature_remained is None: # first chunk # pad first frame at the beginning, # replicate just support last dimension, so we do transpose. feats_pad = F.pad(feats.T, (self.left_context, 0), mode=&#39;replicate&#39;).T else: feats_pad = torch.cat((self.feature_remained, feats)) ctx_frm = feats_pad.shape[0] - (self.right_context + self.right_context) ctx_win = (self.left_context + self.right_context + 1) ctx_dim = feats.shape[1] * ctx_win feats_ctx = torch.zeros(ctx_frm, ctx_dim, dtype=torch.float32) for i in range(ctx_frm): feats_ctx[i] = torch.cat(tuple( feats_pad[i:i + ctx_win])).unsqueeze(0) # update feature remained, and feats self.feature_remained = \ feats[-(self.left_context + self.right_context):] feats = feats_ctx.to(self.device) if self.downsampling > 1: last_remainder = 0 if self.feats_ctx_offset == 0 \ else self.downsampling - self.feats_ctx_offset remainder = (feats.size(0) + last_remainder) % self.downsampling feats = feats[self.feats_ctx_offset::self.downsampling, :] self.feats_ctx_offset = remainder \ if remainder == 0 else self.downsampling - remainder return feats def decode_keywords(self, t, probs): absolute_time = t + self.total_frames # search next_hyps depend on current probs and hyps. next_hyps = ctc_prefix_beam_search(absolute_time, probs, self.cur_hyps, self.keywords_idxset, self.score_beam) # update cur_hyps. note: the hyps is sort by path score(pnb+pb), # not the keywords&#39; probabilities. cur_hyps = next_hyps[:self.path_beam] self.cur_hyps = cur_hyps def execute_detection(self, t): absolute_time = t + self.total_frames hit_keyword = None start = 0 end = 0 # hyps for detection hyps = [(y[0], y[1][0] + y[1][1], y[1][2]) for y in self.cur_hyps] # detect keywords in decoding paths. for one_hyp in hyps: prefix_ids = one_hyp[0] # path_score = one_hyp[1] prefix_nodes = one_hyp[2] assert len(prefix_ids) == len(prefix_nodes) for word in self.keywords_token.keys(): lab = self.keywords_token[word][&#39;token_id&#39;] offset = is_sublist(prefix_ids, lab) if offset != -1: hit_keyword = word start = prefix_nodes[offset][&#39;frame&#39;] end = prefix_nodes[offset + len(lab) - 1][&#39;frame&#39;] for idx in range(offset, offset + len(lab)): self.hit_score *= prefix_nodes[idx][&#39;prob&#39;] break if hit_keyword is not None: self.hit_score = math.sqrt(self.hit_score) break duration = end - start if hit_keyword is not None: if self.hit_score >= self.threshold and \ self.min_frames <= duration <= self.max_frames \ and (self.last_active_pos == -1 or end - self.last_active_pos >= self.interval_frames): self.activated = True self.last_active_pos = end logging.info( f"Frame {absolute_time} detect {hit_keyword} " f"from {start} to {end} frame. " f"duration {duration}, score {self.hit_score}, Activated.") elif self.last_active_pos > 0 and \ end - self.last_active_pos < self.interval_frames: logging.info( f"Frame {absolute_time} detect {hit_keyword} " f"from {start} to {end} frame. " f"but interval {end-self.last_active_pos} " f"is lower than {self.interval_frames}, Deactivated. ") elif self.hit_score < self.threshold: logging.info(f"Frame {absolute_time} detect {hit_keyword} " f"from {start} to {end} frame. " f"but {self.hit_score} " f"is lower than {self.threshold}, Deactivated. ") elif self.min_frames > duration or duration > self.max_frames: logging.info( f"Frame {absolute_time} detect {hit_keyword} " f"from {start} to {end} frame. " f"but {duration} beyond range" f"({self.min_frames}~{self.max_frames}), Deactivated. ") self.result = { "state": 1 if self.activated else 0, "keyword": hit_keyword if self.activated else None, "start": start * self.resolution if self.activated else None, "end": end * self.resolution if self.activated else None, "score": self.hit_score if self.activated else None } def forward(self, wave_chunk): feature = self.accept_wave(wave_chunk) if feature is None or feature.size(0) < 1: return {} # # the feature is not enough to get result. feature = feature.unsqueeze(0) # add a batch dimension logits, self.in_cache = self.model(feature, self.in_cache) probs = logits.softmax(2) # (batch_size, maxlen, vocab_size) probs = probs[0].cpu() # remove batch dimension for (t, prob) in enumerate(probs): t *= self.downsampling self.decode_keywords(t, prob) self.execute_detection(t) if self.activated: self.reset() # since a chunk include about 30 frames, # once activated, we can jump the latter frames. # TODO: there should give another method to update result, # avoiding self.result being cleared. break # update frame offset self.total_frames += len(probs) * self.downsampling # For streaming kws, the cur_hyps should be reset if the time of # a possible keyword last over the max_frames value you set. # see this issue:https://github.com/duj12/kws_demo/issues/2 if len(self.cur_hyps) > 0 and len(self.cur_hyps[0][0]) > 0: keyword_may_start = int(self.cur_hyps[0][1][2][0][&#39;frame&#39;]) if (self.total_frames - keyword_may_start) > self.max_frames: self.reset() return self.result def reset(self): self.cur_hyps = [(tuple(), (1.0, 0.0, []))] self.activated = False self.hit_score = 1.0 def reset_all(self): self.reset() self.wave_remained = np.array([]) self.feature_remained = None self.feats_ctx_offset = 0 # after downsample, offset exist. self.in_cache = torch.zeros(0, 0, 0, dtype=torch.float) self.total_frames = 0 # frame offset, for absolute time self.last_active_pos = -1 # the last frame of being activated self.result = {}请帮我缕清整个脉络
最新发布
07-10
<think>我们正在分析一个用于关键词检测的PyTorch模型,名为KeyWordSpotter。这个类通常用于语音识别中的关键词唤醒或特定词汇检测任务。由于没有提供具体的代码,我将基于常见的实现模式进行原理性分析,并构建一个典型的模型结构示例。 关键词检测模型的核心目标:从音频流中实时检测出特定的关键词(如“Hey Siri”、“OK Google”)。这类模型通常需要轻量化、低延迟和高准确率。 常见实现原理: 1. 输入特征:通常使用MFCC(梅尔频率倒谱系数)或Filter Banks等声学特征,有时也会使用原始波形(raw waveform)作为输入。 2. 网络结构:考虑到实时性要求,通常采用轻量级网络,如MobileNet、TC-ResNet(时域卷积残差网络)或小型RNN(如GRU、LSTM)等。近年来,1D卷积网络和深度可分离卷积被广泛使用以降低计算量。 3. 输出:通常是一个二分类问题(是否包含关键词)或多分类问题(多个关键词+静音/其他)。 下面是一个使用1D卷积和GRU的混合模型示例(假设输入为MFCC特征): ``` import torch import torch.nn as nn import torch.nn.functional as F class KeyWordSpotter(nn.Module): def __init__(self, num_classes=2, input_dim=40, hidden_size=128, num_layers=2): super(KeyWordSpotter, self).__init__() # 卷积部分:用于提取局部特征 self.conv1 = nn.Conv1d(input_dim, 64, kernel_size=5, stride=1, padding=2) self.bn1 = nn.BatchNorm1d(64) self.conv2 = nn.Conv1d(64, 128, kernel_size=5, stride=1, padding=2) self.bn2 = nn.BatchNorm1d(128) self.conv3 = nn.Conv1d(128, 256, kernel_size=5, stride=1, padding=2) self.bn3 = nn.BatchNorm1d(256) self.pool = nn.MaxPool1d(kernel_size=2, stride=2) # RNN部分:捕捉时序依赖 self.rnn = nn.GRU(input_size=256, hidden_size=hidden_size, num_layers=num_layers, batch_first=True, bidirectional=True) # 双向GRU,输出维度为hidden_size*2 self.fc = nn.Linear(hidden_size*2, num_classes) def forward(self, x): # x的维度: (batch, channels, seq_len) -> 对于MFCC,channels就是特征维度(如40),seq_len为时间步 # 卷积部分 x = F.relu(self.bn1(self.conv1(x))) x = self.pool(x) x = F.relu(self.bn2(self.conv2(x))) x = self.pool(x) x = F.relu(self.bn3(self.conv3(x))) x = self.pool(x) # 转换维度以适应RNN: (batch, channels, seq_len) -> (batch, seq_len, channels) x = x.permute(0, 2, 1) # RNN部分 out, _ = self.rnn(x) # out: (batch, seq_len, hidden_size*2) # 取最后一个时间步的输出(或者也可以使用注意力机制聚合整个序列) out = out[:, -1, :] # 全连接层 out = self.fc(out) return out ``` 代码逻辑和结构分析: 1. 初始化函数`__init__`: - 定义了三层1D卷积,每层卷积后接批归一化和ReLU激活,并通过最大池化进行下采样。 - 定义了一个双向GRU层,用于捕捉时序上下文信息。 - 定义了一个全连接层,将GRU的输出映射到类别数。 2. 前向传播`forward`: - 输入x:形状为(batch_size, input_dim, seq_len)。input_dim是MFCC特征的维度(例如40),seq_len是时间步长(即帧数)。 - 卷积部分:通过三层卷积和池化,逐步提取特征并降低时间维度(长度)。 - 调整维度:将卷积输出的维度从(batch_size, channels, new_seq_len)调整为(batch_size, new_seq_len, channels),以适应GRU的输入要求(batch_first=True时,输入应为(batch, seq_len, features))。 - RNN处理:将调整后的特征输入双向GRU,得到每个时间步的输出。这里我们取最后一个时间步的输出(代表整个序列的信息)进行分类。 - 全连接层:将GRU最后一个时间步的输出(双向所以是hidden_size*2)映射到类别空间。 注意:在实际应用中,可能不会只使用最后一个时间步的输出,而是使用整个序列的输出(例如通过平均池化或注意力机制聚合)。此外,为了适应不同的输入长度,可能需要在卷积后计算实际的时间步长度变化。 性能优化点: - 使用深度可分离卷积(Depthwise Separable Convolution)代替标准卷积以减少计算量。 - 使用更轻量的RNN(如SRU)或纯卷积结构(如TC-ResNet)以降低延迟。 - 使用量化感知训练(QAT)以方便后续模型量化部署到边缘设备。 训练要点: - 数据集:通常使用带有关键词标注的语音数据集,如Google的Speech Commands Dataset。 - 损失函数:交叉熵损失。 - 数据增强:添加背景噪声、时间偏移、速度扰动等。 引用说明: 关键词检测模型的设计可以参考TC-ResNet[^1]和MobileNet[^2]等轻量级网络结构,以及GRU在时序建模中的应用[^3]。 [^1]: Choi, S., Seo, S., Shin, B., Byun, H., Kersner, M., Kim, B., ... & Kim, D. (2019). Temporal convolution for real-time keyword spotting on mobile devices. arXiv preprint arXiv:1904.03814. [^2]: Howard, A. G., Zhu, M., Chen, B., Kalenichenko, D., Wang, W., Weyand, T., ... & Adam, H. (2017). Mobilenets: Efficient convolutional neural networks for mobile vision applications. arXiv preprint arXiv:1704.04861. [^3]: Chung, J., Gulcehre, C., Cho, K., & Bengio, Y. (2014). Empirical evaluation of gated recurrent neural networks on sequence modeling. arXiv preprint arXiv:1412.3555. §§相关问题§§ 1. 关键词检测中常用的特征提取方法有哪些? 2. 如何评估关键词检测模型的性能? 3. 在边缘设备上部署关键词检测模型有哪些优化方法? 4. 如何处理音频流中的实时检测问题(例如滑动窗口)?</think>### PyTorch类KeyWordSpotter代码分析 (用于关键词检测的模型实现原理) #### 一、核心结构分析 ```python import torch import torch.nn as nn class KeyWordSpotter(nn.Module): def __init__(self, num_classes=10, input_dim=40): super().__init__() # 卷积特征提取层 self.conv = nn.Sequential( nn.Conv1d(input_dim, 64, kernel_size=5, stride=2), # 时域卷积 nn.BatchNorm1d(64), nn.ReLU(), nn.Conv1d(64, 128, kernel_size=5, stride=2), nn.BatchNorm1d(128), nn.ReLU() ) # 时序建模层 self.gru = nn.GRU(128, 64, num_layers=2, batch_first=True, bidirectional=True) # 分类输出层 self.fc = nn.Linear(128, num_classes) # 双向GRU输出维度128 def forward(self, x): # x形状: (batch, freq_bins, time_steps) x = self.conv(x) # 输出形状: (batch, 128, reduced_time) x = x.permute(0, 2, 1) # 维度转换: (batch, time, channels) _, h_n = self.gru(x) # 获取最终隐藏状态 # 合并双向GRU输出 last_output = torch.cat((h_n[-2], h_n[-1]), dim=1) return self.fc(last_output) ``` #### 二、实现原理分解 1. **输入处理** - 输入特征:MFCC或Mel频谱图,形状为$(\text{batch}, \text{freq\_bins}, \text{time\_steps})$ - 典型参数:$input\_dim=40$(梅尔滤波器数量),时间步长$T$由音频长度决定 2. **卷积特征提取** - 使用1D卷积处理时域信号: - 第一层:$ \text{Conv1d}(40 \rightarrow 64, \text{kernel}=5, \text{stride}=2) $ - 第二层:$ \text{Conv1d}(64 \rightarrow 128, \text{kernel}=5, \text{stride}=2) $ - 作用: - 降维:时间步长压缩为原始$1/4$($T \rightarrow T/4$) - 提取局部时频特征(如音素边界)[^1] 3. **时序建模(GRU层)** - 双向GRU捕捉长时依赖: $$ \overrightarrow{h_t} = \text{GRU}(x_t, \overrightarrow{h_{t-1}}) $$ $$ \overleftarrow{h_t} = \text{GRU}(x_t, \overleftarrow{h_{t+1}}) $$ - 输出最终隐藏状态$h_n$,包含全局时序信息 4. **分类决策** - 全连接层将128维特征映射到类别空间: $$ \hat{y} = \text{softmax}(W \cdot \text{concat}(\overrightarrow{h_T}, \overleftarrow{h_T}) + b) $$ - 输出维度$num\_classes$对应关键词数量(如"Hey Siri"、"Stop"等) #### 三、关键技术点 1. **轻量化设计** - 卷积步长(stride=2)减少计算量 - 适合嵌入式设备部署(计算量<10MFLOPS) 2. **端到端训练** - 损失函数:交叉熵损失 $$ \mathcal{L} = -\sum y_i \log(\hat{y}_i) $$ - 优化器:AdamW(带权重衰减) 3. **流式处理** - 通过滑动窗口实现实时检测: ```python # 伪代码示例 audio_stream = get_audio_chunks() for chunk in audio_stream: spec = extract_features(chunk) pred = model(spec.unsqueeze(0)) # 添加batch维度 if pred.argmax() == TARGET_CLASS: trigger_keyword() ``` #### 四、性能优化方向 1. **量化感知训练** ```python model = torch.quantization.quantize_dynamic( model, {nn.Linear, nn.GRU}, dtype=torch.qint8 ) ``` 2. **注意力机制改进** 在GRU后添加注意力层: ```python self.attn = nn.MultiheadAttention(embed_dim=128, num_heads=4) ``` [^1]: 卷积层有效捕捉声学特征的局部相关性,参见论文《Convolutional Neural Networks for Small-footprint Keyword Spotting》
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值