ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Python实战:从零构建马尔科夫链时序预测模型

Python实战:从零构建马尔科夫链时序预测模型 1. 从“下一步只取决于现在”说起马尔科夫链的直觉理解如果你玩过“大富翁”或者类似的棋盘游戏可能会注意到一个现象你下一回合走到哪个格子只取决于你当前在哪个格子以及你掷出的骰子点数跟你之前走过哪些格子、怎么走过来的完全没有关系。这个“下一步只取决于现在”的特性就是马尔科夫链最核心、最迷人的思想。在时序预测的江湖里当我们面对那些看似随机跳跃、但内在又存在某种“记忆”的数据时比如天气的晴雨转换、股票价格的涨跌、用户点击网页的行为序列马尔科夫链提供了一种极其优雅且强大的建模工具。它不试图去拟合复杂的长期依赖而是聚焦于刻画状态之间最直接的转移概率这种化繁为简的能力让它在很多场景下既实用又好用。今天要聊的就是如何把这个经典的数学模型从理论公式变成我们手中可用的Python预测工具。很多人一听到“马尔科夫链”就觉得是艰深的随机过程理论离实际应用很远。其实不然它的核心概念非常直观实现起来也相当直接。我们将彻底抛开那些让人望而生畏的数学符号堆砌直接从“状态”、“转移”这些生活化的概念入手一步步拆解如何用Python构建一个属于自己的马尔科夫链时序预测模型。你会发现它不仅是学术论文里的常客更是解决许多实际预测问题的“快刀”。2. 马尔科夫链的核心三要素状态、转移与无记忆性要玩转马尔科夫链首先得吃透它的三个基本构件状态空间、转移概率和马尔科夫性。这听起来有点抽象我们用一个非常接地气的例子——每日天气预测——来把它们具象化。2.1 状态空间系统可能处在的所有“位置”想象一下我们简化天气只关心三种情况晴天Sunny、多云Cloudy、雨天Rainy。那么我们这个微型天气系统的状态空间State Space就是集合 {晴天 多云 雨天}。在数学上我们常用S {S1, S2, ..., Sn}来表示这里S1, S2, S3就对应着我们的三种天气。在编程中我们最自然的方式就是用列表或元组来定义它states [Sunny, Cloudy, Rainy]状态的定义是建模的第一步也是最关键的一步。定义得太粗比如只分“好天气”和“坏天气”可能会丢失重要信息导致预测不准定义得太细比如把湿度、风速、云量都组合成无数状态又会使得状态空间爆炸数据稀疏难以估计转移概率。一个经验法则是状态应该是互斥且完备的并且基于业务理解确保每个状态在预测问题中都有明确的意义和区分度。2.2 转移概率矩阵一张“跳转”路线图知道了有哪些“位置”接下来就要知道从当前位置跳到下一个位置的可能性有多大。这就是转移概率Transition Probability。继续我们的天气例子假设根据历史数据统计我们发现如果今天是晴天明天有70%的概率还是晴天20%的概率转多云10%的概率下雨。如果今天多云明天有30%的概率转晴50%的概率维持多云20%的概率下雨。如果今天下雨明天有20%的概率转晴30%的概率转多云50%的概率继续下雨。我们可以把这些概率整理成一张表格也就是转移概率矩阵Transition Probability Matrix, TPM。矩阵的行表示当前状态列表示下一时刻的状态每个单元格Pij就表示从状态i转移到状态j的概率。当前状态 \ 下一状态晴天(S)多云(C)雨天(R)晴天(S)0.70.20.1多云(C)0.30.50.2雨天(R)0.20.30.5用数学公式表示即Pij P(下一时刻状态为 j | 当前时刻状态为 i)。并且对于矩阵的每一行所有概率之和必须等于1因为从当前状态出发下一时刻必然转移到状态空间中的某个状态包括自身。在Python里我们可以用NumPy数组或Pandas DataFrame来优雅地表示它import numpy as np import pandas as pd # 定义状态 states [Sunny, Cloudy, Rainy] # 定义转移概率矩阵 transition_matrix np.array([ [0.7, 0.2, 0.1], # 从Sunny出发 [0.3, 0.5, 0.2], # 从Cloudy出发 [0.2, 0.3, 0.5] # 从Rainy出发 ]) # 转换为DataFrame更易读 df_tpm pd.DataFrame(transition_matrix, indexstates, columnsstates) print(df_tpm)2.3 马尔科夫性关键的“无记忆”假设前面提到的“下一步只取决于现在”在数学上称为马尔科夫性Markov Property或无记忆性Memoryless Property。严格来说对于一个随机过程{X_t}如果给定当前状态X_t未来状态X_{t1}的条件概率分布与过去状态{X_0, X_1, ..., X_{t-1}}无关那么这个过程就具有马尔科夫性。用公式表达就是P(X_{t1} j | X_t i, X_{t-1} i_{t-1}, ..., X_0 i_0) P(X_{t1} j | X_t i)。这个假设是马尔科夫链模型的基石它极大地简化了建模的复杂度。我们不需要去考虑长长的历史序列只需要盯着当前状态就行。当然这也是模型的局限性所在。现实世界中很多过程是具有长期记忆或周期性的比如经济周期、季节性天气纯粹的马尔科夫链可能无法完美刻画。因此在应用时我们需要审视数据是否大致满足“近期状态影响最大”的特性或者考虑使用高阶马尔科夫链未来状态取决于最近几个状态等扩展模型。注意在构建转移概率矩阵时务必确保每一行的概率之和为1。这是一个常见的错误检查点。如果使用历史数据统计可能会出现某一行和不为1的情况通常是由于数据缺失或统计误差需要进行归一化处理Pij_normalized Pij / sum(Pij)。3. 从历史数据到转移矩阵模型训练的核心步骤理论很美好但模型不会凭空产生。一个能用的马尔科夫链预测模型其灵魂——转移概率矩阵——必须从真实的历史数据中学习得到。这个过程本质上就是统计。我们以一份简单的每日天气记录序列为例演示如何一步步计算出转移矩阵。假设我们有以下为期15天的天气观测序列S代表晴天C代表多云R代表雨天S, S, C, R, C, C, S, S, S, R, R, C, S, C, R我们的目标是统计从每一个状态转移到另一个状态的频次然后将其转化为概率。3.1 第一步构建状态转移频次矩阵我们初始化一个3x3的零矩阵行和列都对应[S, C, R]。然后遍历历史序列对于每一对相邻的日子(今天明天)在对应的矩阵单元格上加1。遍历过程(S, S) -count[S][S] 1(S, C) -count[S][C] 1(C, R) -count[C][R] 1(R, C) -count[R][C] 1(C, C) -count[C][C] 1(C, S) -count[C][S] 1(S, S) -count[S][S] 1(S, S) -count[S][S] 1(S, R) -count[S][R] 1(R, R) -count[R][R] 1(R, C) -count[R][C] 1(C, S) -count[C][S] 1(S, C) -count[S][C] 1(C, R) -count[C][R] 1统计完成后我们得到频次矩阵From \ ToSCR行和S3216C2125R0213这里行和表示从该状态出发的总转移次数。注意因为序列有15天所以我们有14次转移观测。S作为起点的转移发生了6次C5次R3次总和14验证无误。3.2 第二步频次矩阵归一化为概率矩阵接下来将频次矩阵的每一行除以该行的总和就得到了转移概率矩阵。对于行S:[3/6, 2/6, 1/6] [0.5, 0.333..., 0.166...]对于行C:[2/5, 1/5, 2/5] [0.4, 0.2, 0.4]对于行R:[0/3, 2/3, 1/3] [0.0, 0.666..., 0.333...]最终的概率矩阵为From \ ToSCRS0.5000.3330.167C0.4000.2000.400R0.0000.6670.333注意从R雨天转移到S晴天的概率为0这是因为在我们这个很小的样本数据中没有观察到“雨天之后直接是晴天”的情况。这引出了一个小样本下的常见问题零概率问题。如果某个转移从未在历史数据中出现模型会认为它不可能发生这在实际预测中可能是危险的因为它无法处理未见过的情况。为了解决这个问题可以考虑使用平滑技术比如拉普拉斯平滑Laplace Smoothing即在所有频次上加一个很小的数如1然后再计算概率确保没有零值出现。3.3 Python代码实现自动化训练过程下面是一个完整的Python函数它接收一个状态序列列表自动计算并返回转移概率矩阵带拉普拉斯平滑选项。import numpy as np from collections import defaultdict def train_markov_chain(sequence, statesNone, smoothing0): 根据状态序列训练马尔科夫链转移概率矩阵。 参数: sequence: list, 状态序列如 [S, S, C, R, ...] states: list, 可选所有可能的状态列表。如果为None则从sequence中提取唯一值。 smoothing: float, 平滑因子拉普拉斯平滑默认为0不平滑。 返回: trans_matrix: numpy.ndarray, 转移概率矩阵。 state_index: dict, 状态到索引的映射。 if states is None: states sorted(list(set(sequence))) n_states len(states) # 创建状态到索引的映射 state_index {state: i for i, state in enumerate(states)} # 初始化频次矩阵使用浮点数以便后续做平滑加法 count_matrix np.zeros((n_states, n_states), dtypefloat) # 统计转移频次 for i in range(len(sequence) - 1): current_state sequence[i] next_state sequence[i 1] row state_index[current_state] col state_index[next_state] count_matrix[row, col] 1 # 应用拉普拉斯平滑 if smoothing 0: count_matrix count_matrix smoothing # 计算行和每个状态出发的总次数 row_sums count_matrix.sum(axis1, keepdimsTrue) # 避免除以零如果某行和为零则设置该行概率均匀分布 row_sums[row_sums 0] 1 # 归一化得到概率矩阵 trans_matrix count_matrix / row_sums return trans_matrix, state_index # 使用示例 historical_sequence [S, S, C, R, C, C, S, S, S, R, R, C, S, C, R] # 不带平滑 tpm_no_smooth, state_idx train_markov_chain(historical_sequence, states[S, C, R]) print(转移概率矩阵无平滑:) print(tpm_no_smooth) print(\n状态索引:, state_idx) # 带拉普拉斯平滑加1平滑 tpm_smooth, _ train_markov_chain(historical_sequence, states[S, C, R], smoothing1) print(\n转移概率矩阵拉普拉斯平滑smoothing1:) print(tpm_smooth)运行这段代码你会看到平滑后的矩阵中从R到S的概率不再为0而是一个很小的正数约0.083这更符合实际情况——雨天之后总有可能放晴。4. 让模型动起来单步预测与多步模拟有了训练好的转移概率矩阵我们的模型就具备了“预测”的能力。预测主要分为两种单步预测和多步模拟状态序列生成。4.1 单步预测基于当前状态看下一步单步预测回答的问题是“已知今天是状态i明天最可能是哪个状态” 这非常简单我们只需要查看转移概率矩阵的第i行选择概率最大的那个状态即可。def predict_next_state(current_state, trans_matrix, state_index): 给定当前状态预测下一时刻最可能的状态。 参数: current_state: str, 当前状态。 trans_matrix: numpy.ndarray, 转移概率矩阵。 state_index: dict, 状态到索引的映射。 返回: predicted_state: str, 预测的下一状态。 probability: float, 预测状态对应的概率。 if current_state not in state_index: raise ValueError(f状态 {current_state} 不在已知状态列表中。) row_idx state_index[current_state] # 获取当前状态对应的转移概率行 prob_row trans_matrix[row_idx, :] # 找到最大概率的索引 next_state_idx np.argmax(prob_row) # 根据索引找到状态名 index_to_state {v: k for k, v in state_index.items()} predicted_state index_to_state[next_state_idx] probability prob_row[next_state_idx] return predicted_state, probability # 使用示例 current R pred_state, pred_prob predict_next_state(current, tpm_smooth, state_idx) print(f当前状态为 {current}预测下一状态为 {pred_state}概率为 {pred_prob:.3f})在这个平滑后的例子里当前状态是R雨天模型会查看矩阵的R行[0.083, 0.583, 0.333]发现转移到C多云的概率最高0.583因此预测明天多云。4.2 多步模拟生成未来的可能状态序列很多时候我们不仅想知道下一步还想看看未来一段时间内可能的状态演变路径。这就是序列生成或多步模拟。其核心是迭代从初始状态开始根据转移概率矩阵随机采样出下一个状态然后将这个新状态作为当前状态重复这个过程。这里的关键是“随机采样”。我们不是每次都选概率最大的而是按照概率分布进行随机选择这样才能模拟出各种可能的未来路径反映出过程的不确定性。Python的numpy.random.choice函数非常适合做这件事。def simulate_markov_chain(initial_state, trans_matrix, state_index, n_steps10): 模拟马尔科夫链未来n步的状态序列。 参数: initial_state: str, 初始状态。 trans_matrix: numpy.ndarray, 转移概率矩阵。 state_index: dict, 状态到索引的映射。 n_steps: int, 要模拟的未来步数。 返回: sequence: list, 生成的状态序列包含初始状态。 if initial_state not in state_index: raise ValueError(f初始状态 {initial_state} 不在已知状态列表中。) index_to_state {v: k for k, v in state_index.items()} sequence [initial_state] current_state initial_state for _ in range(n_steps): row_idx state_index[current_state] prob_row trans_matrix[row_idx, :] # 按照概率分布随机选择下一个状态 next_state_idx np.random.choice(len(prob_row), pprob_row) next_state index_to_state[next_state_idx] sequence.append(next_state) current_state next_state return sequence # 使用示例从晴天开始模拟未来7天的天气 np.random.seed(42) # 设置随机种子使结果可复现 initial S simulated_sequence simulate_markov_chain(initial, tpm_smooth, state_idx, n_steps7) print(f初始状态为 {initial}模拟的未来7天天气序列) print( - .join(simulated_sequence))运行这段代码你可能会得到像S - S - C - R - C - S - S - C这样的序列。每次运行结果都可能不同这正是随机模拟的特点。通过运行成千上万次模拟我们可以统计未来某一天处于各个状态的概率分布这比单一路径的预测包含了更丰富的信息。实操心得在进行多步模拟时随机种子的设置很重要。在开发调试阶段固定随机种子如np.random.seed(42)可以确保每次运行结果一致便于验证逻辑。但在最终需要随机性的场景如蒙特卡洛模拟则应移除种子设置。另外对于长期模拟需要注意马尔科夫链可能存在的稳态分布。有些链在经过足够多的步数后状态分布会趋于稳定不再依赖于初始状态。我们可以通过计算转移矩阵的特征向量来求得这个稳态分布这对于理解系统的长期行为非常有帮助。5. 从分类到预测处理连续数值型时序数据前面的例子中状态是离散的类别晴、雨。但现实中很多时序数据是连续的数值比如股票价格、气温、销售额。如何用马尔科夫链来预测这些数据核心思路是离散化。我们将连续的数值范围划分成若干个区间每个区间视为一个状态。这样就把一个回归问题转化为了一个分类问题。5.1 状态划分的艺术分箱策略离散化也叫分箱Binning是这一步成败的关键。分箱方法直接影响状态的定义和转移概率的估计。等宽分箱将数据范围均匀分成N个区间。例如温度范围是-10°C到40°C分成5个箱每个箱宽10°C。优点是简单但可能对数据分布不敏感如果数据集中在某个区域会导致某些箱内数据很多某些箱内数据很少。等频分箱分位数分箱使每个箱内的数据点数量大致相等。例如将数据分成5份每份包含20%的数据。这能保证每个状态都有足够的数据来估计转移概率避免了数据稀疏问题是更常用的方法。基于业务知识分箱根据实际意义划分。比如股票涨跌幅可以划分为“大跌(-5%)”、“小跌(-5%~0)”、“持平(0)”、“小涨(0~5%)”、“大涨(5%)”。让我们用一份模拟的每日销售额数据来演示。import pandas as pd import numpy as np # 生成模拟销售额数据万元 np.random.seed(123) n_days 200 # 假设有一个缓慢上升的趋势加上季节性波动和随机噪声 trend np.linspace(10, 15, n_days) seasonality 2 * np.sin(2 * np.pi * np.arange(n_days) / 30) noise np.random.randn(n_days) * 1.5 sales trend seasonality noise sales np.maximum(sales, 0) # 销售额非负 dates pd.date_range(start2023-01-01, periodsn_days, freqD) sales_df pd.DataFrame({Date: dates, Sales: sales}) print(sales_df.head())5.2 实现连续数据的马尔科夫链预测流程接下来我们使用等频分箱四分位数将销售额离散化为4个状态Low,Medium-Low,Medium-High,High。def continuous_to_markov(data_series, n_bins4, bin_namesNone): 将连续时序数据转换为马尔科夫链状态序列。 参数: data_series: pandas.Series, 连续数值序列。 n_bins: int, 分箱数量。 bin_names: list, 可选每个箱子的标签。如果为None则自动生成。 返回: state_series: pandas.Series, 离散化后的状态序列。 bins: list, 分箱的边界值。 # 使用分位数进行等频分箱 state_series, bins pd.qcut(data_series, qn_bins, labelsFalse, retbinsTrue, duplicatesdrop) # 处理分箱数量可能因重复值而减少的情况 actual_n_bins len(bins) - 1 if bin_names is None: bin_names [fState_{i} for i in range(actual_n_bins)] elif len(bin_names) ! actual_n_bins: raise ValueError(fbin_names长度({len(bin_names)})与实际分箱数({actual_n_bins})不匹配。) # 将数值标签映射为状态名 state_series pd.Series([bin_names[int(i)] for i in state_series], indexdata_series.index) return state_series, bins, bin_names # 应用分箱 sales_series sales_df.set_index(Date)[Sales] state_series, bin_edges, state_labels continuous_to_markov(sales_series, n_bins4, bin_names[Low, Medium-Low, Medium-High, High]) print(分箱边界:, bin_edges) print(\n前10天的状态:) print(state_series.head(10)) sales_df[State] state_series.values现在我们有了离散的状态序列。接下来用之前定义的train_markov_chain函数来训练转移概率矩阵并进行预测。# 训练模型 state_sequence state_series.tolist() tpm_sales, state_idx_sales train_markov_chain(state_sequence, statesstate_labels, smoothing0.1) # 使用小量平滑 print(销售额马尔科夫链转移概率矩阵:) tpm_df pd.DataFrame(tpm_sales, indexstate_labels, columnsstate_labels) print(tpm_df.round(3)) # 假设今天是‘Medium-High’状态预测明天 current_sales_state Medium-High next_state, prob predict_next_state(current_sales_state, tpm_sales, state_idx_sales) print(f\n当前销售额状态为 {current_sales_state}预测下一状态为 {next_state}概率为 {prob:.3f}) # 模拟未来5天的状态路径 np.random.seed(456) sim_states simulate_markov_chain(current_sales_state, tpm_sales, state_idx_sales, n_steps5) print(f\n从 {current_sales_state} 开始的模拟路径: { - .join(sim_states)})5.3 从状态预测反推数值预测我们预测出了明天的状态是“High”但这还不够我们可能想知道“High”状态对应的销售额大概是多少。一个简单的方法是使用该状态对应原始数据点的中心趋势如中位数或均值作为预测值。def state_to_value_prediction(state, state_series, value_series, methodmedian): 将状态预测转换为数值预测。 参数: state: str, 预测的状态。 state_series: pandas.Series, 历史状态序列。 value_series: pandas.Series, 对应的历史数值序列需与state_series同索引。 method: str, 聚合方法median 或 mean. 返回: predicted_value: float, 预测的数值。 # 找出历史上所有处于该状态的数据点 mask state_series state if not mask.any(): return np.nan state_values value_series[mask] if method median: return float(state_values.median()) elif method mean: return float(state_values.mean()) else: raise ValueError(method 必须是 median 或 mean) # 将状态序列和销售额序列对齐确保索引一致 aligned_state_series sales_df.set_index(Date)[State] aligned_value_series sales_df.set_index(Date)[Sales] # 假设我们预测明天状态为‘High’ predicted_state High predicted_sales state_to_value_prediction(predicted_state, aligned_state_series, aligned_value_series, methodmedian) print(f预测状态 {predicted_state} 对应的销售额中位数约为: {predicted_sales:.2f} 万元)踩坑提醒离散化是连续数据应用马尔科夫链的最大不确定性来源。分箱的数量和策略需要仔细斟酌。箱数太少模型过于粗糙丢失信息箱数太多状态空间变大每个状态下的历史数据变少导致转移概率估计不可靠数据稀疏问题。一个实用的建议是先用等频分箱并确保每个状态至少有几十个历史数据点。可以通过交叉验证来评估不同分箱数对预测精度的影响。此外对于有明显趋势或季节性的数据直接对原始值分箱可能不是最佳选择。有时先去除趋势/季节性对残差序列建模或者对差分序列如日环比变化率进行分箱效果会更好。6. 评估、局限与实战进阶思考模型建好了也能跑出预测结果了但我们怎么知道它好不好用它又在什么情况下会“失灵”呢这是在实际项目中必须面对的问题。6.1 如何评估马尔科夫链预测模型对于分类预测状态预测我们可以使用标准的分类评估指标。将历史数据按时间顺序分成训练集和测试集用训练集训练转移矩阵然后在测试集上进行一步预测并与真实状态比较。from sklearn.metrics import accuracy_score, classification_report, confusion_matrix # 假设我们已经有了完整的状态序列 full_state_sequence split_ratio 0.8 split_idx int(len(full_state_sequence) * split_ratio) train_seq full_state_sequence[:split_idx] test_seq full_state_sequence[split_idx:] # 在训练集上训练模型 tpm_train, state_idx_train train_markov_chain(train_seq, smoothing0.1) # 在测试集上进行一步预测 predictions [] true_labels [] for i in range(len(test_seq) - 1): current_state test_seq[i] true_next_state test_seq[i 1] if current_state in state_idx_train: # 确保当前状态在训练集中出现过 pred_state, _ predict_next_state(current_state, tpm_train, state_idx_train) predictions.append(pred_state) true_labels.append(true_next_state) # 计算准确率 if predictions: accuracy accuracy_score(true_labels, predictions) print(f一步预测准确率: {accuracy:.3f}) print(\n分类报告:) print(classification_report(true_labels, predictions, zero_division0)) # 可以进一步查看混淆矩阵 # cm confusion_matrix(true_labels, predictions, labelslist(state_idx_train.keys())) # print(cm) else: print(没有足够的数据进行预测评估。)对于数值预测则可以使用回归指标如均方误差MSE、平均绝对误差MAE等将状态预测反推的数值与真实值进行比较。6.2 马尔科夫链的局限性什么时候它可能不适用理解模型的局限性和适用边界比盲目应用更重要。马尔科夫性假设过强这是最根本的局限。如果真实过程具有长期记忆如长周期、自回归特性或者未来状态严重依赖于多个过去状态一阶马尔科夫链的预测效果会很差。例如股票价格常常具有波动聚集性Volatility Clustering今天的波动会影响未来很多天简单的MC模型难以捕捉。状态空间定义困难对于复杂、高维的数据如何定义有意义且可处理的状态空间是一个挑战。离散化会丢失信息。静态转移矩阵我们训练出的转移概率矩阵是固定的假设转移规律不随时间变化。但对于非平稳过程其统计特性随时间变化这显然不成立。例如经济周期不同阶段市场状态的转移模式可能完全不同。数据需求与稀疏性为了可靠地估计转移概率每个状态都需要有足够多的观测样本。如果状态很多或数据量不足会导致概率估计不准。6.3 实战进阶模型优化与扩展思路当基础模型效果不佳时可以考虑以下方向进行优化或扩展高阶马尔科夫链这是最直接的扩展。在n阶马尔科夫链中下一状态的概率依赖于前n个状态。这能捕捉更长的历史依赖但代价是状态空间呈指数级增长状态数变为M^n其中M是基础状态数。需要更多的数据和更复杂的学习算法。隐马尔可夫模型HMM这是一个巨大的飞跃。在HMM中状态是不可直接观测的“隐”状态我们只能看到由状态生成的观测值。HMM通过 Baum-Welch 等算法可以同时学习状态转移概率和观测概率。它非常适合那些我们认为有隐藏机制驱动观测序列的场景比如语音识别隐藏状态是音素观测是声学特征、金融市场状态识别隐藏状态是“牛市”、“熊市”、“震荡市”观测是价格、成交量等。时变转移矩阵如果过程是非平稳的可以尝试将时间划分为多个窗口如按月、按年在每个窗口内分别训练一个转移矩阵。或者使用更复杂的模型如马尔科夫切换模型Markov Switching Models让转移概率本身也随时间或其它变量变化。结合其他特征可以将马尔科夫链与其他模型结合。例如先用其他模型如ARIMA、LSTM预测趋势再用马尔科夫链对残差波动部分进行建模捕捉状态切换的模式。在我处理一个用户页面浏览行为预测的项目时最初使用一阶MC准确率只有60%左右。后来分析发现用户下一步点击什么不仅取决于当前页面还和上一步的页面强相关比如从首页到列表页和从详情页回列表页接下来的行为可能不同。我们尝试了二阶马尔科夫链虽然状态数从几十个增加到上千个导致了一些数据稀疏问题通过平滑缓解但预测准确率提升到了75%以上。这个案例说明对业务逻辑的深入理解是选择和改进模型的第一驱动力。不要被模型的数学形式吓住多从“它假设了什么”、“我的数据符合这个假设吗”这两个问题出发去思考往往能找到正确的方向。
返回列表