鈺潔 發表於 2025-8-20 14:49:00

彩笔运维勇闯机器学习--多元线性回归(实战)

<h2 id="前言">前言</h2>
<p>书接上文,上一小节简单介绍了多元回归的基本原理、使用方式,本小节来实践:qps与cpu、内存、磁盘io、网络io之间的关系</p>
<h2 id="获取数据">获取数据</h2>
<p>参考一元线性回归的获取方式</p>
<pre><code>from flow import *
from datetime import datetime

start_time = datetime.strptime('2025-04-06 00:00:00', '%Y-%m-%d %H:%M:%S').timestamp()
end_time = datetime.strptime('2025-04-06 23:59:59', '%Y-%m-%d %H:%M:%S').timestamp()
step = 600
sls_step = 3600*6

query = get_query_data(start_time, end_time, sls_step)
cpu = get_cpu_data(start_time, end_time, step)
memory = get_memory_data(start_time, end_time, step)
network_in = get_network_in_data(start_time, end_time, step)
network_out = get_network_out_data(start_time, end_time, step)
file_read = get_file_read_data(start_time, end_time, step)
file_write = get_file_write_data(start_time, end_time, step)

print('cpu 数据个数为{} ,前10数据为{}'.format(len(cpu), cpu[:10]))
print('query 数据个数为{} ,前10数据为{}'.format(len(query), query[:10]))
print('memory 数据个数为{} ,前10数据为{}'.format(len(memory), memory[:10]))
print('network_in 数据个数为{} ,前10数据为{}'.format(len(network_in), network_in[:10]))
print('network_out 数据个数为{} ,前10数据为{}'.format(len(network_out), network_out[:10]))
print('file_read 数据个数为{} ,前10数据为{}'.format(len(file_read), file_read[:10]))
print('file_write 数据个数为{} ,前10数据为{}'.format(len(file_write), file_write[:10]))
</code></pre>
<p>脚本!启动:</p>
<p><img alt="watermarked-multi_regression_2_1" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202508/1416773-20250820143123734-1591712300.png" class="lazyload"></p>
<h2 id="特征标准化">特征标准化</h2>
<p>特征数据已经获取完成,看起来是没问题,但是仔细分析,好像又有点问题,首先cpu数据非常小,内存数据又很大,特征数据之间的数量级差距太大了,特别是在多元回归中,不同特征的量纲和尺度可能差异巨大。若未标准化, 回归系数的数值大小会受特征尺度影响,导致难以直接比较特征的重要性</p>
<p>那首先先人为的进行数据缩放</p>
<pre><code>query =
cpu = get_cpu_data(start_time, end_time, step)
memory =
network_in =
network_out =
file_read =
file_write =
</code></pre>
<p>看看效果</p>
<p><img alt="watermarked-multi_regression_2_2" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202508/1416773-20250820143151884-1674096587.png" class="lazyload"></p>
<p>调整过后,特征数据都是2位数的了,只不过单位不一样</p>
<ul>
<li>query的单位是万</li>
<li>memory的单位是G</li>
<li>network_in的单位是M</li>
<li>network_out的单位是M</li>
<li>file_read的单位是K</li>
<li>file_write的单位是K</li>
</ul>
<p>再进行数据标准化,数据标准化的公式</p>
<p></p><div class="math display">\[z = \frac{x - \mu}{\sigma}
\]</div><p></p><ul>
<li>μ 是特征的均值,</li>
<li>σ 是特征的标准差。</li>
</ul>
<p>标准化后,所有特征的尺度统一,均值为0,标准差为1</p>
<pre><code>from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
</code></pre>
<h2 id="训练模型">训练模型</h2>
<h4 id="多元回归启动">多元回归!启动</h4>
<pre><code>from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.preprocessing import StandardScaler
from flow import *
from datetime import datetime
import pandas as pd

def adjusted_r2(r2, n, p):
    return 1 - (1 - r2) * (n - 1) / (n - p - 1)

start_time = datetime.strptime('2025-04-06 00:00:00', '%Y-%m-%d %H:%M:%S').timestamp()
end_time = datetime.strptime('2025-04-06 23:59:59', '%Y-%m-%d %H:%M:%S').timestamp()
step = 600
sls_step = 3600*6

query =
cpu = get_cpu_data(start_time, end_time, step)
memory =
network_in =
network_out =
file_read =
file_write =

features = {
    'feature1': cpu,
    'feature2': memory,
    'feature3': network_in,
    'feature4': network_out,
    'feature5': file_read,
    'feature6': file_write,
}


data = {
    'result': query,
}

data.update(features)

df = pd.DataFrame(data)

X = df[[
    'feature1',
    'feature2',
    'feature3',
    'feature4',
    'feature5',
    'feature6',
]]
y = df['result']

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

model = LinearRegression()
model.fit(X, y)

y_pred = model.predict(X)
MSE = mean_squared_error(y, y_pred)
r2 = r2_score(y, y_pred)
print("R²:", r2)
print("MSE:", MSE)

p = len(features.keys())
n = len(data['result'])
adjusted_r2 = adjusted_r2(r2, n, p)
print(f"调整决定系数 (Adjusted R²): {adjusted_r2}")

</code></pre>
<p>脚本!启动:</p>
<p><img alt="watermarked-multi_regression_2_3" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202508/1416773-20250820143205935-1610860405.png" class="lazyload"></p>
<p>完美的模型,来得太顺利反而有点不太习惯了</p>
<p>通过lasso回归来看下哪一些参数是强相关的</p>
<pre><code>from sklearn.linear_model import Lasso
lasso = Lasso(alpha=0.1)
lasso.fit(X_scaled, y)

for i, coef in enumerate(lasso.coef_, 1):
    print(f'x{i} 的回归系数:{coef:.4f}')

</code></pre>
<h4 id="lasso回归检查特征">lasso回归检查特征</h4>
<p>脚本!启动:</p>
<p><img alt="watermarked-multi_regression_2_4" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202508/1416773-20250820143228490-1407407026.png" class="lazyload"></p>
<p>这里面已经有一些特征在划水了,找出来,把他们裁掉!当然lasso回归已经自动帮我们把假装干活的特征自动裁员了(打工人,哭死=_=!),但是还是有必要找出原因来的</p>
<p><img alt="watermarked-multi_regression_2_5" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202508/1416773-20250820143235896-590135247.png" class="lazyload"></p>
<p>将这4个特征画个图看一下</p>
<p>X2(内存)、X5(file_read)、X6(file_write)符合预期:特征是一条直线,不随着query波动</p>
<ul>
<li>首先看X2,这是内存。在业务服务中,由于是java服务,内存在启动的时候划分了一大块交给jvm管理,所以在操作系统看来,变化不大,要折腾都在jvm内部折腾。所以memory是一条直线</li>
<li>X5,这是file_read。在业务服务中,几乎没有的file_read,都是通过network_read,所以file_read一条直线</li>
<li>X6,这是file_write。在业务服务中,都是缓存写,再由操作系统同步到磁盘,所以file_write是由操作系统决定的,近似一条直线</li>
</ul>
<p>再看X3,这是network_in,按理说这个特征是与query强相关的,有明显的数据波动,但是lasso回归的时候还是把它略掉了,认为它是一个多余的特征</p>
<h2 id="t检验">t检验</h2>
<h4 id="定义">定义</h4>
<p>检验每个自变量是否真正影响了结果。更直接一点,揪出谁在工作谁在划水!</p>
<h4 id="实践">实践</h4>
<p>先介绍一个专门用于数据分析的工具:statsmodels,用来做统计推断,并且提供额外的模型检查工具</p>
<p>安装也很简单:</p>
<pre><code>pip3 install statsmodels
</code></pre>
<p>添加代码:</p>
<pre><code>import statsmodels.api as sm

X_with_const = sm.add_constant(X)
sm_model = sm.OLS(y, X_with_const).fit()
print(sm_model.summary())
</code></pre>
<p>脚本!启动:</p>
<p><img alt="watermarked-multi_regression_2_6" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202508/1416773-20250820143244426-1183777013.png" class="lazyload"></p>
<p>这怎么还越整越复杂了!这输出的都是些什么东西啊?!</p>
<p>别着急,这部分主要是描述t检验,先拿出t检验相关的数据</p>
<p><img alt="watermarked-multi_regression_2_7" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202508/1416773-20250820143252357-2033522202.png" class="lazyload"></p>
<p>简单解释一下:</p>
<ul>
<li>第一列就是参数,const是常数项,也就是公式中的<span class="math inline">\(β_0\)</span>,其余的是6个参数</li>
<li>第二列coef,是所谓的系数,就是<span class="math inline">\(β_1 β_2 \dots β_n\)</span></li>
<li>第三列std err,就是所谓的标准误差</li>
<li>第四列t值,计算公式为:</li>
</ul>
<p></p><div class="math display">\[t=\frac{coef}{std err}
\]</div><p></p><ul>
<li>第五列P&gt;|t|,所谓的P值,计算公式就不列出来了,我自己都没搞明白 =_=!,只要记住非常有用就行,一会会用</li>
<li>最后两列一起看,这是所谓的置信区间,什么置信区间?正态分布熟悉吧,记住置信区间和正态分布强相关就行了,<pre><code>feature1      31.2856      9.573      3.268      0.001      12.356      50.215
</code></pre>
这里意味着,有95%的系数是落在 之间</li>
</ul>
<p>说了这么半天,最直接有效的就是看P值,越接近0,那就说明该系数越相关</p>
<p>还有个更简单的方法,直接丢gpt看看</p>
<p><img alt="watermarked-multi_regression_2_8" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202508/1416773-20250820143259579-1922214931.png" class="lazyload"></p>
<p>ai牛逼,科技提高工作效率。至此,通过t检验,也可以发现有哪些特征是强相关的,哪些特征是无用的。feature1(cpu)、feature3(network_in)、feature4(network_out)这3个特征对于结果有重大的影响。之前的lasso回 归中,feature3(network_in)已经被判定了对于结果没有重大影响</p>
<p>为什么这两个评估工具给出了不一样的答案呢?</p>
<h2 id="多重共线性">多重共线性</h2>
<p>回归模型中两个或多个预测变量(自变量)之间存在高度相关性的情况。针对于我们的这个模型,很容易联想到feature3(network_in)、feature4(network_out),是高度相关性的</p>
<h4 id="vif方差膨胀因子">VIF(方差膨胀因子)</h4>
<pre><code>from statsmodels.stats.outliers_influence import variance_inflation_factor

vif_df = pd.DataFrame()
vif_df['features'] = X.columns
vif_df['VIF'] = )]
print(vif_df)

</code></pre>
<p><img alt="watermarked-multi_regression_2_11" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202508/1416773-20250820143309546-623833938.png" class="lazyload"></p>
<p>VIF &gt; 10,说明存在严重的多重共线性</p>
<h4 id="相关系数矩阵">相关系数矩阵</h4>
<pre><code>corr_matrix = df.corr()
print(corr_matrix)
</code></pre>
<p><img alt="watermarked-multi_regression_2_9" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202508/1416773-20250820143316156-139218369.png" class="lazyload"></p>
<p>直接丢进gpt</p>
<p><img alt="watermarked-multi_regression_2_10" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202508/1416773-20250820143322126-1489731289.png" class="lazyload"></p>
<h4 id="小结">小结</h4>
<p>根据上述分析,query可以直接通过cpu特征就可以分析出来了,其他的特征存在与否,其实并不重要</p>
<p>在statsmodels的报告中已经揭示了我们的模型存在严重的共线性:</p>
<p><img alt="watermarked-multi_regression_2_12" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202508/1416773-20250820143328462-402768603.png" class="lazyload"></p>
<p><code>Cond. No.</code>这个值大于1000的时候,就表示存在了严重的多重共线性问题</p>
<p>多重共线性带来的问题:</p>
<ul>
<li>回归系数估计变得不稳定,小的数据变化可能导致系数大幅变化</li>
<li>系数标准误增大,t统计量减小,导致变量可能显得不显著</li>
<li>难以区分单个变量对因变量的独立影响</li>
<li>虽然预测可能仍然准确,但解释单个变量的影响变得困难</li>
</ul>
<h2 id="ridge岭回归">ridge(岭回归)</h2>
<h4 id="数学模型">数学模型</h4>
<p></p><div class="math display">\[\mathcal{L} = \frac{1}{2n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 + \lambda \sum_{j=1}^{p} β_j^2
\]</div><p></p><h4 id="实践-1">实践</h4>
<pre><code>from sklearn.linear_model import Ridge

ridge = Ridge(alpha=1.0)
ridge.fit(X_scaled, y)
y_ridge = ridge.predict(X_scaled)
r2_ridge = r2_score(y, y_ridge)
r2_adj_ridge = adjusted_r2(r2_ridge, n, 7)

for i, coef in enumerate(ridge.coef_, 1):
    print(f'x{i} 的回归系数:{coef:.4f}')

print("\nridge模型:")
print(f"R² = {r2_ridge:.4f}, Adjusted R² = {r2_adj_ridge:.4f}")

</code></pre>
<p><img alt="watermarked-multi_regression_2_13" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202508/1416773-20250820143337596-825393359.png" class="lazyload"></p>
<p>ridge回归中,所有系数都会被压缩向零但不会完全为零,所以不像lasso回归中直接为<code>0</code>那么直观:</p>
<ul>
<li>绝对值较大的系数通常对应更重要的特征</li>
<li>正负号表示特征与目标变量的正/负相关关系</li>
</ul>
<p>如图所示,X1与X4是更为重要的特征,这与lasso回归得出的结论是一样的</p>
<h2 id="总结">总结</h2>
<p>多元回归的复杂性,是由于特征参数过多带来的新的问题,无用特征、多重共线性,要找出特征的权重,要用到一些检验方法,比如t检验、VIF、相关矩阵系数等。如果只想关注模型泛化能力,可以通过lasso、ridge等回归来自动筛选特征</p>
<p>综上所述,本次多元回归之旅最终的结果又回到了一元回归,饶了一大圈又回到了原点,但是并非毫无收获,除了收获了一大堆模型评估方法,什么调整决定系数、t检验、VIF等,还有一堆陌生的检测算法,lasso回归、ridge回归等。并且提供了今后对于多元回归的一些方法论</p>
<h2 id="装杯时刻">装杯时刻</h2>
<p>那位兄弟问了,你这洋洋洒洒搞了这么半天,怎么装杯呢?</p>
<p>今天这个就不给老板汇报了,毕竟如果只关注结果的话,又回到了一元回归的方法论来,但是依然有价值分享给同事,毕竟这一堆猛烈的方法输出,每月一次的团队培训内部培训,这不是又有题材了不是吗?</p>
<h2 id="联系我">联系我</h2>
<ul>
<li>联系我,做深入的交流</li>
</ul>
<p><img alt="" width="500" height="200" loading="lazy" src="https://img2024.cnblogs.com/blog/1416773/202411/1416773-20241121135740959-1907948957.png#" class="lazyload"></p>
<hr>
<p>至此,本文结束<br>
在下才疏学浅,有撒汤漏水的,请各位不吝赐教...</p>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:it排球君,转载请注明原文链接:https://www.cnblogs.com/MrVolleyball/p/19048691</p>
<div>本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。 </div><br><br>
来源:https://www.cnblogs.com/MrVolleyball/p/19048691
頁: [1]
查看完整版本: 彩笔运维勇闯机器学习--多元线性回归(实战)