feat: fix py code for tratitional import path error
This commit is contained in:
parent
08c9950db5
commit
4684216c85
|
|
@ -3,10 +3,14 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import joblib
|
import joblib
|
||||||
import jieba
|
import jieba
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
# 获取根模块路径(ml-module目录)
|
||||||
|
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
# 分类映射(根据数据库中的分类)
|
# 分类映射(根据数据库中的分类)
|
||||||
CATEGORY_MAP = {
|
CATEGORY_MAP = {
|
||||||
'ENTERTAINMENT': '娱乐',
|
'ENTERTAINMENT': '娱乐',
|
||||||
|
|
@ -17,15 +21,20 @@ CATEGORY_MAP = {
|
||||||
'AUTOMOTIVE': '汽车',
|
'AUTOMOTIVE': '汽车',
|
||||||
'GOVERNMENT': '政务',
|
'GOVERNMENT': '政务',
|
||||||
'HEALTH': '健康',
|
'HEALTH': '健康',
|
||||||
'AI': 'AI'
|
'AI': 'AI',
|
||||||
|
'HOUSE': '房产'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class TraditionalPredictor:
|
class TraditionalPredictor:
|
||||||
"""传统机器学习预测器"""
|
"""传统机器学习预测器"""
|
||||||
|
|
||||||
def __init__(self, model_type='nb', model_dir='../../models/traditional'):
|
def __init__(self, model_type='nb', model_dir='./models/traditional'):
|
||||||
self.model_type = model_type
|
self.model_type = model_type
|
||||||
|
# 如果是相对路径,则基于根模块路径
|
||||||
|
if not os.path.isabs(model_dir):
|
||||||
|
self.model_dir = os.path.join(ROOT_DIR, model_dir)
|
||||||
|
else:
|
||||||
self.model_dir = model_dir
|
self.model_dir = model_dir
|
||||||
self.vectorizer = None
|
self.vectorizer = None
|
||||||
self.classifier = None
|
self.classifier = None
|
||||||
|
|
@ -85,10 +94,96 @@ def predict_single(title: str, content: str, model_type='nb') -> Dict[str, Any]:
|
||||||
return predictor.predict(title, content)
|
return predictor.predict(title, content)
|
||||||
|
|
||||||
|
|
||||||
|
def print_probabilities_table(probabilities: Dict[str, float]):
|
||||||
|
"""
|
||||||
|
表格方式打印各分类概率
|
||||||
|
"""
|
||||||
|
# 按概率降序排序
|
||||||
|
sorted_probs = sorted(probabilities.items(), key=lambda x: x[1], reverse=True)
|
||||||
|
|
||||||
|
# 表头
|
||||||
|
print("\n📊 各分类预测概率")
|
||||||
|
print("=" * 50)
|
||||||
|
print(f"{'分类代码':<15}{'分类名称':<10}{'概率':>10}")
|
||||||
|
print("-" * 50)
|
||||||
|
|
||||||
|
# 表体
|
||||||
|
for code, prob in sorted_probs:
|
||||||
|
name = CATEGORY_MAP.get(code, '未知')
|
||||||
|
print(f"{code:<15}{name:<10}{prob * 100:>8.2f}%")
|
||||||
|
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# 测试
|
# 测试
|
||||||
result = predict_single(
|
result = predict_single(
|
||||||
title="华为发布新款折叠屏手机",
|
title="BBA在华销量,跌回七八年前",
|
||||||
content="华为今天正式发布了新一代折叠屏手机,搭载最新麒麟芯片..."
|
content="""
|
||||||
|
中新经纬1月15日电 (龚宸芫)近日,BBA(宝马、奔驰、奥迪)公布了2025年全球销量。数据显示,宝马集团全球销量为246.37万辆,同比微增0.5%;梅赛德斯-奔驰全球销量为216万辆,同比下降10%;奥迪全球销量为162.36万辆,同比下降2.9%。
|
||||||
|
聚焦中国市场,宝马中国2025年销量为62.55万辆,同比下滑12.5%。奔驰中国销量为57.5万辆,同比下滑19.5%。奥迪中国销量为61.75万辆,同比下滑5%。同2024年相比,三家车企销量减幅合计接近26万辆。
|
||||||
|
上述数据显示,2025年,BBA中国市场销量水平回到了七八年前。
|
||||||
|
根据各家官网披露的数据,宝马、奔驰、奥迪2018年在华销量分别为64万辆、67.41万辆,以及66.09万辆,2017年则分别为59.44万辆、58.79万辆,以及59.79万辆。
|
||||||
|
BBA近十年在华销量 中新经纬制图
|
||||||
|
部分主销车型每月少卖近3000辆
|
||||||
|
|
||||||
|
虽然奔驰和宝马在华销量下降幅度都超过了10%,但从官方数据来看,其最大的单一市场还是中国。其中,宝马中国在其全球销量体系中占比为25.4%,奔驰中国在其全球销量体系中占比为26.5%。
|
||||||
|
|
||||||
|
谈及中国市场销量下滑原因,奔驰给出的解释是,其在中国市场面临豪华车市场结构性波动及自身产品周期调整等挑战。
|
||||||
|
|
||||||
|
中国汽车流通协会专家委员会委员章弘告诉中新经纬,中国品牌产销量迅速增长,尤其是新能源品牌在30万元以上市场中抢占份额,直接挑战了BBA原有地位。此外,特斯拉等品牌在新能源领域的布局也对BBA形成压力,尤其在中高端市场,特斯拉的产品力和品牌影响力都在逐渐增强。
|
||||||
|
|
||||||
|
比如,根据懂车帝统计的终端数据,问界M9自2025年3月底上市以来,持续在50万元以上的SUV市场取得头部表现,全年销量接近12万辆。而与之价格相接近的宝马X5,全年销量为7.01万辆,比2024年低1.7万辆。
|
||||||
|
|
||||||
|
“BBA电动化转型滞后也是其销量下滑的原因之一,BBA的新能源车型占比相对较低,奔驰纯电动车占比仅8.1%,奥迪也刚过12.9%,而中国市场新能源渗透率已达47%。消费者对新能源车型的需求增长,使得BBA的传统燃油车优势逐渐减弱。”章弘说。
|
||||||
|
|
||||||
|
与2024年相比,从主销产品销量来看,根据第三方平台懂车帝数据,宝马、奔驰、奥迪在华各有不同表现。
|
||||||
|
|
||||||
|
其中,宝马中国主销轿车在2025年实现不同程度涨幅,而SUV普遍呈下降趋势。比如宝马3系2025年比2024年多卖了1.19万辆车,宝马5系多卖2.74万辆,而宝马X3 2025年比2024年少卖3.34万辆,宝马X1也少卖3万辆。
|
||||||
|
|
||||||
|
奔驰中国的主销车型,不管是SUV还是轿车,都有不同程度下滑,其中下滑最多的是奔驰C级,比2024年下降了3.54万辆,平均每个月少卖了0.295万辆。GLC则是2025年比2024年少卖了2.74万辆,奔驰E级和奔驰GLB分别少卖了1.47万辆和1.74万辆。
|
||||||
|
|
||||||
|
奥迪中国是三家中下滑程度最轻的车企,变动最大的主销车型是奥迪A4L,从2024年的10.74万辆降至2025年的8万辆,少卖出2.74万辆。而其他大部分主销车型下降幅度在0.5万辆到1.5万辆之间。不过,奥迪A3销量实现了上涨,从2024年的5.48万辆增至2025年的6.68万辆,多卖了1.2万辆。
|
||||||
|
|
||||||
|
值得一提的是,公开资料显示,2025年年中,奥迪A3在终端市场曾进行了一次幅度较大的降价,该车指导价为16.59万到20.99万元,当时在4S店端裸车价跌破13万元,根据各大购车平台显示的车主成交价,最低成交价为11万元出头,比官方指导价低5万元。
|
||||||
|
|
||||||
|
“汽车市场的价格体系与性价比正在发生变化,BBA入门级车型在20万~40万元区间面临中国品牌的激烈竞争,价格优势不再明显。消费者在购车时更注重性价比,中国品牌在配置、智能化等方面的表现更具吸引力。并且,BBA部分车型存在配置选装费用较高的问题,导致实际购车成本上升,进一步削弱竞争力。此外,消费者需求变化、市场环境与政策调整,都会影响到BBA销量。”章弘分析道。
|
||||||
|
|
||||||
|
1月初,中新经纬探访位于北京的多家宝马4S店,看到大部分主销车型现车都有选装费用,如宝马5系、3系,即使是基础选装也会比裸车价格贵上万元。
|
||||||
|
|
||||||
|
三家均将推出多款新能源车
|
||||||
|
|
||||||
|
从公开披露的信息来看,BBA三家车企决定2026年继续加大投入中国市场,尤其在新能源车领域。
|
||||||
|
|
||||||
|
宝马集团在2026年总体规划中提到,将为中国消费者带来约20款BMW、MINI和BMW摩托车新产品。其中,国产长轴距版新世代BMW iX3将于2026年上半年全球首发,并在下半年上市,该车将搭载宝马集团首创的全景iDrive、BMW新世代电驱系统、BMW驾控超级大脑等技术。
|
||||||
|
|
||||||
|
按照梅赛德斯-奔驰的计划,其2026年将向中国市场引入超15款全新和改款产品,覆盖新生代豪华、核心豪华和高端豪华细分市场,以及多种驱动方式。如旗下三大全新纯电架构平台MB.EA、AMG.EA、VAN.EA均有新车陆续亮相。
|
||||||
|
|
||||||
|
对于奥迪中国而言,2026年亦是产品大年。其中,一汽奥迪在2025年底表示,2026年将推出全新奥迪Q5L、奥迪A6L e-tron、全新奥迪A6L等车型。
|
||||||
|
|
||||||
|
上汽奥迪计划在2026年推出奥迪E5 Sportback钦定性能quattro型、奥迪E7X等车型,后者计划于2026年北京车展亮相,并在同年上半年上市;此外,新品牌AUDI E SUV概念车的量产版也将在2026年上市。
|
||||||
|
|
||||||
|
BBA已做好进击准备,中国豪华市场2026年将迎来怎样的竞争格局?
|
||||||
|
|
||||||
|
章弘认为,自主品牌在30万元以上高端市场占比将会持续提升,比亚迪、吉利、长安、奇瑞等传统车企通过高端子品牌与新势力车企共同发力,凭借电动化、智能化技术优势和本土化创新,抢占传统豪华品牌市场份额。
|
||||||
|
|
||||||
|
“预计2026年自主高端车型销量占比将进一步扩大,形成与BBA等传统豪华品牌分庭抗礼的格局。传统豪华品牌将加速本土化研发,推出更多电动车型,只是产品竞争力仍需时间提升。部分品牌可能通过降价、优化渠道等方式维持甚至提高销量。”章弘表示。
|
||||||
|
|
||||||
|
公开资料显示,2026年开年之初,宝马中国宣布对旗下超30款主力车型进行建议零售价调整,其中24款车型的降幅超10%,6款超20%,部分车型最高官降30万元。
|
||||||
|
|
||||||
|
“我们对2026年中国豪华车市场保持审慎乐观态度,预计市场将延续2025年态势,整体竞争强度持续高位,市场整合与分化将进一步加剧。同时,2026年将是豪华车消费税起征点下调后的第一个完整实施年份,其对市场结构的深远影响将全面显现。在经历了前几年高速增长后,市场进入调整期是正常的。”近日,保时捷中国总裁潘励驰对中新经纬表示。
|
||||||
|
|
||||||
|
罗兰贝格1月8日发布的《预见2026:中国行业趋势报告》显示,从阵营格局看,新势力与民营企业的份额已分别从2023年的3.6%和24.6%上升至2025年的8.8%和33.1%,外资品牌份额则出现快速下滑。然而,这一趋势未必会持续放大。随着合资车企加强与本土供应链的合作,其在智能化方面的短板有望补足,市场份额或将逐步止跌回稳。
|
||||||
|
|
||||||
|
聚焦BBA,章弘认为:“BBA也许会加速本土化转型,如加大电动化产品投放、提升智能化配置等,其市场份额下跌趋势可能得到一定遏制,跌幅较往年收窄。同时,BBA有可能会继续推进渠道优化,收缩低效能网点,同时加强数字化展厅建设和服务体验提升。”
|
||||||
|
|
||||||
|
公开资料显示,2025年以来,BBA旗下多家中国4S店闭店。比如,2025年3月到8月,宝马中国撤销了数十家4S店的授权,其中包括北京最大的宝马4S店。2025年4月,奔驰宝利德旗下三家门店闭店。
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
print(result)
|
print("\n🎯 最终预测结果")
|
||||||
|
print("=" * 50)
|
||||||
|
print(f"预测分类 : {result['categoryName']} ({result['categoryCode']})")
|
||||||
|
print(f"置信度 : {result['confidence'] * 100:.2f}%")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
# 表格打印概率
|
||||||
|
print_probabilities_table(result['probabilities'])
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import jieba
|
import jieba
|
||||||
import joblib
|
import joblib
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
@ -14,6 +15,9 @@ from sklearn.svm import SVC
|
||||||
from sklearn.model_selection import train_test_split
|
from sklearn.model_selection import train_test_split
|
||||||
from sklearn.metrics import classification_report, accuracy_score, f1_score
|
from sklearn.metrics import classification_report, accuracy_score, f1_score
|
||||||
|
|
||||||
|
# 获取根模块路径(ml-module目录)
|
||||||
|
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
# 分类映射(与数据库表一致)
|
# 分类映射(与数据库表一致)
|
||||||
CATEGORY_MAP = {
|
CATEGORY_MAP = {
|
||||||
'ENTERTAINMENT': '娱乐',
|
'ENTERTAINMENT': '娱乐',
|
||||||
|
|
@ -24,7 +28,8 @@ CATEGORY_MAP = {
|
||||||
'AUTOMOTIVE': '汽车',
|
'AUTOMOTIVE': '汽车',
|
||||||
'GOVERNMENT': '政务',
|
'GOVERNMENT': '政务',
|
||||||
'HEALTH': '健康',
|
'HEALTH': '健康',
|
||||||
'AI': 'AI'
|
'AI': 'AI',
|
||||||
|
'HOUSE': '房产'
|
||||||
}
|
}
|
||||||
|
|
||||||
REVERSE_CATEGORY_MAP = {v: k for k, v in CATEGORY_MAP.items()}
|
REVERSE_CATEGORY_MAP = {v: k for k, v in CATEGORY_MAP.items()}
|
||||||
|
|
@ -40,7 +45,8 @@ CATEGORY_STOPWORDS = {
|
||||||
'AUTOMOTIVE': {'汽车', '车型', '上市', '发布', '销量', '市场', '品牌', '厂商'},
|
'AUTOMOTIVE': {'汽车', '车型', '上市', '发布', '销量', '市场', '品牌', '厂商'},
|
||||||
'GOVERNMENT': {'会议', '讲话', '指出', '强调', '部署', '落实', '推进', '要求', '精神', '决定', '意见', '方案', '安排'},
|
'GOVERNMENT': {'会议', '讲话', '指出', '强调', '部署', '落实', '推进', '要求', '精神', '决定', '意见', '方案', '安排'},
|
||||||
'HEALTH': {'医生', '专家', '建议', '提示', '提醒', '研究', '发现', '可能', '有助于'},
|
'HEALTH': {'医生', '专家', '建议', '提示', '提醒', '研究', '发现', '可能', '有助于'},
|
||||||
'AI': {'技术', '系统', '模型', '算法', '应用', '功能', '版本', '升级', '研发', '推出', '人工智能'}
|
'AI': {'技术', '系统', '模型', '算法', '应用', '功能', '版本', '升级', '研发', '推出', '人工智能'},
|
||||||
|
'HOUSE': {'楼盘', '房价', '房产', '二手房', '新房', '学区房', '租房', '购房', '按揭', '首付', '房贷', '小区', '物业', '开发商', '售楼处', '开盘', '交房', '产权', '房产证', '不动产'}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -65,10 +71,12 @@ class NewsClassifier:
|
||||||
if self.use_stopwords:
|
if self.use_stopwords:
|
||||||
self._load_stopwords()
|
self._load_stopwords()
|
||||||
|
|
||||||
def _load_stopwords(self, stopwords_path='../../data/news_stopwords.txt'):
|
def _load_stopwords(self, stopwords_path=None):
|
||||||
"""
|
"""
|
||||||
加载停用词表
|
加载停用词表
|
||||||
"""
|
"""
|
||||||
|
if stopwords_path is None:
|
||||||
|
stopwords_path = os.path.join(ROOT_DIR, 'data/news_stopwords.txt')
|
||||||
try:
|
try:
|
||||||
with open(stopwords_path, 'r', encoding='utf-8') as f:
|
with open(stopwords_path, 'r', encoding='utf-8') as f:
|
||||||
self.stopwords = set(line.strip() for line in f if line.strip())
|
self.stopwords = set(line.strip() for line in f if line.strip())
|
||||||
|
|
@ -82,6 +90,11 @@ class NewsClassifier:
|
||||||
:param text: 待处理文本
|
:param text: 待处理文本
|
||||||
:param category: 可选,指定分类时使用分类专属停用词
|
:param category: 可选,指定分类时使用分类专属停用词
|
||||||
"""
|
"""
|
||||||
|
# 处理 NaN 值或空值
|
||||||
|
if pd.isna(text) or text is None:
|
||||||
|
return ''
|
||||||
|
if not isinstance(text, str):
|
||||||
|
text = str(text)
|
||||||
# 移除多余空格和换行
|
# 移除多余空格和换行
|
||||||
text = ' '.join(text.split())
|
text = ' '.join(text.split())
|
||||||
# jieba分词
|
# jieba分词
|
||||||
|
|
@ -108,6 +121,9 @@ class NewsClassifier:
|
||||||
"""
|
"""
|
||||||
从CSV文件加载训练数据
|
从CSV文件加载训练数据
|
||||||
"""
|
"""
|
||||||
|
# 如果是相对路径,则基于根模块路径
|
||||||
|
if not os.path.isabs(csv_path):
|
||||||
|
csv_path = os.path.join(ROOT_DIR, csv_path)
|
||||||
df = pd.read_csv(csv_path)
|
df = pd.read_csv(csv_path)
|
||||||
# 合并标题和内容作为特征
|
# 合并标题和内容作为特征
|
||||||
df['text'] = df['title'] + ' ' + df['content']
|
df['text'] = df['title'] + ' ' + df['content']
|
||||||
|
|
@ -205,6 +221,9 @@ class NewsClassifier:
|
||||||
"""
|
"""
|
||||||
保存模型
|
保存模型
|
||||||
"""
|
"""
|
||||||
|
# 如果是相对路径,则基于根模块路径
|
||||||
|
if not os.path.isabs(model_dir):
|
||||||
|
model_dir = os.path.join(ROOT_DIR, model_dir)
|
||||||
os.makedirs(model_dir, exist_ok=True)
|
os.makedirs(model_dir, exist_ok=True)
|
||||||
joblib.dump(self.vectorizer, os.path.join(model_dir, f'{self.model_type}_vectorizer.pkl'))
|
joblib.dump(self.vectorizer, os.path.join(model_dir, f'{self.model_type}_vectorizer.pkl'))
|
||||||
joblib.dump(self.classifier, os.path.join(model_dir, f'{self.model_type}_classifier.pkl'))
|
joblib.dump(self.classifier, os.path.join(model_dir, f'{self.model_type}_classifier.pkl'))
|
||||||
|
|
@ -214,6 +233,9 @@ class NewsClassifier:
|
||||||
"""
|
"""
|
||||||
加载模型
|
加载模型
|
||||||
"""
|
"""
|
||||||
|
# 如果是相对路径,则基于根模块路径
|
||||||
|
if not os.path.isabs(model_dir):
|
||||||
|
model_dir = os.path.join(ROOT_DIR, model_dir)
|
||||||
self.vectorizer = joblib.load(os.path.join(model_dir, f'{self.model_type}_vectorizer.pkl'))
|
self.vectorizer = joblib.load(os.path.join(model_dir, f'{self.model_type}_vectorizer.pkl'))
|
||||||
self.classifier = joblib.load(os.path.join(model_dir, f'{self.model_type}_classifier.pkl'))
|
self.classifier = joblib.load(os.path.join(model_dir, f'{self.model_type}_classifier.pkl'))
|
||||||
print(f"模型已从 {model_dir} 加载")
|
print(f"模型已从 {model_dir} 加载")
|
||||||
|
|
@ -224,12 +246,12 @@ if __name__ == '__main__':
|
||||||
classifier = NewsClassifier(model_type='nb')
|
classifier = NewsClassifier(model_type='nb')
|
||||||
|
|
||||||
# 假设有训练数据文件
|
# 假设有训练数据文件
|
||||||
train_data_path = '../../data/processed/training_data.csv'
|
train_data_path = './data/processed/training_data.csv'
|
||||||
|
|
||||||
if os.path.exists(train_data_path):
|
if os.path.exists(train_data_path):
|
||||||
df = classifier.load_data(train_data_path)
|
df = classifier.load_data(train_data_path)
|
||||||
classifier.train(df)
|
classifier.train(df)
|
||||||
classifier.save_model('../../models/traditional')
|
classifier.save_model('./models/traditional')
|
||||||
|
|
||||||
# 测试预测
|
# 测试预测
|
||||||
test_title = "高效办成一件事”"
|
test_title = "高效办成一件事”"
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ if __name__ == '__main__':
|
||||||
loader = DataLoader(db_url)
|
loader = DataLoader(db_url)
|
||||||
|
|
||||||
# 从数据库加载数据并保存到本地
|
# 从数据库加载数据并保存到本地
|
||||||
data = loader.update_local_data(limit=2000)
|
data = loader.update_local_data(limit=5000)
|
||||||
|
|
||||||
if data is not None:
|
if data is not None:
|
||||||
print(f"成功加载数据,共 {len(data)} 条记录")
|
print(f"成功加载数据,共 {len(data)} 条记录")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue