ML-For-Beginners:用 NLTK 与 VADER 对 51 万条酒店评论做过滤、标签特征化与情感分析的完整实战
本篇基于 ML-For-Beginners 课程 NLP 模块的第 5 课,讲清一条完整的文本数据处理管线:如何把 51 万条、17 列的欧洲酒店评论原始数据集,经过列清洗、地址归一、Tags 标签特征工程和 NLTK 停用词优化,最终用 VADER 规则情感分析器产出带正负情感分列的 Hotel_Reviews_NLP.csv。读完本篇,你可以复现“原始 CSV → 过滤数据集 → NLP 增强数据集”的三步流水线,并理解每一步的 pandas 技巧(groupby.transform、melt、str.split)与性能考量。
课程定位与数据管线总览
这一课属于 NLP 模块 的第 5 节“NLTK for Sentiment Analysis”,承接第 4 节 酒店评论数据准备与探索 中对数据集的逐列分析。整个挑战的假设场景是:你要构建一个酒店推荐机器人,利用情感分析和评分来给出建议。原始数据集是约 230 MB(解压后)的 515K Hotel Reviews Data in Europe(Kaggle 上 CC0 公共领域授权的 Booking.com 爬取数据,由 Jiashen Liu 发布),包含 6 个欧洲城市、上千家酒店的 51 万多条评论。按 数据目录说明,需要把该数据集下载后放入课程的 /data 文件夹。
整条管线在 README 结尾被明确归纳为四步,后处理时建议按此顺序执行:
- 原始数据集
Hotel_Reviews.csv在上一课中被 探索笔记本 探查; - 用 过滤笔记本 对
Hotel_Reviews.csv做列过滤与重算,产出Hotel_Reviews_Filtered.csv; - 用 情感分析笔记本 处理
Hotel_Reviews_Filtered.csv,产出Hotel_Reviews_NLP.csv; - 将
Hotel_Reviews_NLP.csv用于本课结尾的 NLP Challenge。
其中第 2 步内部还夹了一个“标签探索”环节,对应仓库中的 标签处理笔记本(README 正文中只给了思路,具体的正则过滤代码在这个 notebook 里,下文会展开)。
运行环境要求:能运行 Python 3 的 Jupyter notebook,以及 pandas 与 NLTK(NLTK 需本地安装,并在代码中执行 nltk.download('vader_lexicon') 下载情感词典)。
第一步:基础列清洗与地址归一
README 开篇指出数据集存在明显问题:一些列充满无用信息,另一些列的数值(如 Average_Score、Total_Number_of_Reviews)无法通过自己的计算独立验证。因此本步的目标是“多清理一点数据:加入后面有用的列、改写其他列的值、彻底删掉某些列”。
1. 删掉经纬度,把地址压缩成城市级
第一组操作是丢弃 lat、lng(做地图可视化不是本任务目标),然后对 Hotel_Address 做归一化——如果地址里包含城市和国名,就统一替换成“城市, 国家”的短格式。数据集中只有以下 6 组城市/国家:
- Amsterdam, Netherlands
- Barcelona, Spain
- London, United Kingdom
- Milan, Italy
- Paris, France
- Vienna, Austria
README 给出的实现如下(过滤笔记本 中的版本多了一个 else 分支:六国都匹配不上时返回原始地址,避免产生空值):
def replace_address(row):
if "Netherlands" in row["Hotel_Address"]:
return "Amsterdam, Netherlands"
elif "Barcelona" in row["Hotel_Address"]:
return "Barcelona, Spain"
elif "United Kingdom" in row["Hotel_Address"]:
return "London, United Kingdom"
elif "Milan" in row["Hotel_Address"]:
return "Milan, Italy"
elif "France" in row["Hotel_Address"]:
return "Paris, France"
elif "Vienna" in row["Hotel_Address"]:
return "Vienna, Austria"
# Replace all the addresses with a shortened, more useful form
df["Hotel_Address"] = df.apply(replace_address, axis = 1)
# The sum of the value_counts() should add up to the total number of reviews
print(df["Hotel_Address"].value_counts())
注意 README 特意提醒:value_counts() 各值之和应等于评论总数,这是验证替换没有丢行的快速自查手段。归一化之后,就可以直接按“国家/城市”粒度聚合了:
display(df.groupby("Hotel_Address").agg({"Hotel_Name": "nunique"}))
| Hotel_Address | Hotel_Name |
|---|---|
| Amsterdam, Netherlands | 105 |
| Barcelona, Spain | 211 |
| London, United Kingdom | 400 |
| Milan, Italy | 162 |
| Paris, France | 458 |
| Vienna, Austria | 158 |
2. 用 groupby.transform 重算不可验证的元数据列
对酒店元数据列,README 的处理是:
- 丢弃
Additional_Number_of_Scoring(该列仅表示“只打分没写文字”的评论数,无分析价值); - 用“该酒店在数据集中实际出现的评论数”替换
Total_Number_of_Reviews; - 用自算的
Reviewer_Score均值替换Average_Score。
# Drop `Additional_Number_of_Scoring`
df.drop(["Additional_Number_of_Scoring"], axis = 1, inplace=True)
# Replace `Total_Number_of_Reviews` and `Average_Score` with our own calculated values
df.Total_Number_of_Reviews = df.groupby('Hotel_Name').transform('count')
df.Average_Score = round(df.groupby('Hotel_Name').Reviewer_Score.transform('mean'), 1)
这里的两个 transform 是关键:与 agg 会把每行折叠成一行不同,transform('count') / transform('mean') 会把分组统计结果按原索引对齐地广播回每一行,因此替换后 DataFrame 仍是 51.5 万行、每行带着自己酒店的全量统计值,可以直接参与后续逐行操作。这正是“让每家酒店的每一条评论都能引用该酒店聚合值”的 pandas 惯用手法。
3. 评论列与评论者列的去留
- 丢弃
Review_Total_Negative_Word_Counts、Review_Total_Positive_Word_Counts、Review_Date、days_since_review——上一课已经论证过单纯词数不等于情感、日期新鲜度也不在本次分析范围内; - 原样保留
Reviewer_Score、Negative_Review、Positive_Review; Tags暂时保留——它还要在下个小节做过滤后才能丢弃;- 丢弃
Total_Number_of_Reviews_Reviewer_Has_Given(数据里没有评论者唯一 ID,无法把多条评论关联到同一个人,该列无法支撑建模); - 保留
Reviewer_Nationality。
第二步:把列表文本形式的 Tags 变成可用的 0/1 特征
为什么 Tags 列“问题很大”但值得处理
Tags 列以“文本形式存的 Python 列表”存储,例如 [' Business trip ', ' Solo traveler ', ' Single Room ', ' Stayed 5 nights ', ' Submitted from a mobile device ']。它的难点在于:
- 子项个数和顺序不固定(有的行 5 个、有的 3 个、有的 6 个),导致按位置统计会出错,某些酒店可能拿不到本应属于它的标签;
- 数据量大到难以人工识别值得关注的短语:515,000 行、1,427 家酒店,每家可选的标签组合还略有差异——这正是 NLP 的用武之地:扫描文本、统计最常见短语。
- 我们关心的不是单词而是多词短语(如 Business trip)。对全部约 6,762,646 个词跑多词频率分布算法耗时巨大,但探索式分析(EDA)帮了忙:既然样本里能看到标签本身就是逗号分隔的现成短语,就可以大幅减少处理量。
先按业务目标决定“哪些标签值得留”
README 强调:标签的去留取决于数据集的最终用途。这里的用途是“给推荐机器人补充有助于选酒店的特征”,据此给出五条判断:
- 旅行类型(type of trip)相关 → 保留;
- 客人群体类型(type of guest group)重要 → 保留;
- 房间/套房/公寓类型无关(各酒店的房间本质上大同小异)→ 剔除;
- 提交评论所用的设备无关 → 剔除;
- 入住晚数 也许 与“住得久就更喜欢”相关,但牵强,大概率无关 → 剔除。
一句话总结:保留 2 类标签(旅行类型、客人群体),其余全部移出统计范围。
清洗:先去掉括号和引号
计数之前要先把标签变成更规整的格式——去掉方括号和引号。README 明确要求“最快的方式”,因为 51 万行数据的逐行处理不能慢,而 pandas 恰好内置了矢量化字符串操作:
# Remove opening and closing brackets
df.Tags = df.Tags.str.strip("[']")
# remove all quotes too
df.Tags = df.Tags.str.replace(" ', '", ",", regex = False)
处理后每个单元格的形态变为:Business trip, Solo traveler, Single Room, Stayed 5 nights, Submitted from a mobile device。
用“位置不固定”反打:6 个临时列 + melt 全局计数
由于同一短语在不同行里所处的位置不同,直接按列计数会失真。README 的解法是把这个乱序变废为宝:利用“每个标签都是多词短语、且彼此用逗号分隔”这一事实,先按逗号把每个单元格拆成最多 6 个临时列 Tag_1~Tag_6,再把 6 列合并成 1 个大列跑 value_counts(),即可得到不分位置的全局短语频次。结果显示共有 2,428 个唯一标签,高频样本如下:
| Tag | Count |
|---|---|
| Leisure trip | 417778 |
| Submitted from a mobile device | 307640 |
| Couple | 252294 |
| Stayed 1 night | 193645 |
| Stayed 2 nights | 133937 |
| Solo traveler | 108545 |
| Stayed 3 nights | 95821 |
| Business trip | 82939 |
| Group | 65392 |
| Family with young children | 61015 |
| Stayed 4 nights | 47817 |
| Double Room | 35207 |
| Standard Double Room | 32248 |
| Superior Double Room | 31393 |
| Family with older children | 26349 |
| Deluxe Double Room | 24823 |
| Double or Twin Room | 22393 |
| Stayed 5 nights | 20845 |
| Standard Double or Twin Room | 17483 |
| Classic Double Room | 16989 |
| Superior Double or Twin Room | 13570 |
| 2 rooms | 12393 |
README 提示:像 Submitted from a mobile device 这种高频标签虽然无用,但“剔除”这一步本身非常快,可以留着不删、分析时忽略即可。
从源码看,标签处理笔记本 里这一步的具体实现是:先 df.Tags.str.split(',', expand=True) 拆列、逐列 str.strip() 去首尾空格,再用 df.melt(value_vars=["Tag_1", ..., "Tag_6"]) 把宽表摊平成长表(输出形状为 (2514684, 2),即约 251 万条标签实例),然后一条正则把房间类、入住时长类、设备类标签整体过滤掉,并只保留出现次数大于 1000 的标签:
df_tags = df_tags[~df_tags.value.str.contains("Standard|room|Stayed|device|Beds|Suite|Studio|King|Superior|Double", na=False, case=False)]
tag_vc = df_tags.value.value_counts().reset_index(name="count").query("count > 1000")
过滤后的最终频次(README 未展示、notebook 输出中的真实结果)是:
| index | count |
|---|---|
| Leisure trip | 338423 |
| Couple | 205305 |
| Solo traveler | 89779 |
| Business trip | 68176 |
| Group | 51593 |
| Family with young children | 49318 |
| Family with older children | 21509 |
| Travelers with friends | 1610 |
| With a pet | 1078 |
剔除“入住晚数”与“房型”两类标签
README 分别用两张表展示了被移出统计范围的标签:
入住晚数(Length of stay)——注意:是“不再把它们作为要计数/保留的取值来考虑”,而不是从数据集里删掉:
| Length of stay | Count |
|---|---|
| Stayed 1 night | 193645 |
| Stayed 2 nights | 133937 |
| Stayed 3 nights | 95821 |
| Stayed 4 nights | 47817 |
| Stayed 5 nights | 20845 |
| Stayed 6 nights | 9776 |
| Stayed 7 nights | 7399 |
| Stayed 8 nights | 2502 |
| Stayed 9 nights | 1293 |
| ... | ... |
房型(Type of room)——种类极多但语义几乎等价,对选酒店没帮助:
| Type of room | Count |
|---|---|
| Double Room | 35207 |
| Standard Double Room | 32248 |
| Superior Double Room | 31393 |
| Deluxe Double Room | 24823 |
| Double or Twin Room | 22393 |
| Standard Double or Twin Room | 17483 |
| Classic Double Room | 16989 |
| Superior Double or Twin Room | 13570 |
最终保留的 8 个有用标签
经过上面几步(README 原文:这一步“令人愉快”,因为几乎没花多少处理量),最终留下:
| Tag | Count |
|---|---|
| Leisure trip | 417778 |
| Couple | 252294 |
| Solo traveler | 108545 |
| Business trip | 82939 |
| Group (combined with Travellers with friends) | 67535 |
| Family with young children | 61015 |
| Family with older children | 26349 |
| With a pet | 1405 |
其中 Travellers with friends 与 Group 语义基本一致,合并处理是合理选择。
为每个标签生成 0/1 列
最后一步是“标签 → 列”:为每个保留的标签新建一列,逐行检查 Tags 是否包含该短语,包含记 1、否则记 0。这样每家酒店在聚合层面就得到了“多少位评论者把这家酒店当作商务/休闲/带宠物等场景选择”的计数,对推荐非常有价值:
# Process the Tags into new columns
# The file Hotel_Reviews_Tags.py, identifies the most important tags
# Leisure trip, Couple, Solo traveler, Business trip, Group combined with Travelers with friends,
# Family with young children, Family with older children, With a pet
df["Leisure_trip"] = df.Tags.apply(lambda tag: 1 if "Leisure trip" in tag else 0)
df["Couple"] = df.Tags.apply(lambda tag: 1 if "Couple" in tag else 0)
df["Solo_traveler"] = df.Tags.apply(lambda tag: 1 if "Solo traveler" in tag else 0)
df["Business_trip"] = df.Tags.apply(lambda tag: 1 if "Business trip" in tag else 0)
df["Group"] = df.Tags.apply(lambda tag: 1 if "Group" in tag or "Travelers with friends" in tag else 0)
df["Family_with_young_children"] = df.Tags.apply(lambda tag: 1 if "Family with young children" in tag else 0)
df["Family_with_older_children"] = df.Tags.apply(lambda tag: 1 if "Family with older children" in tag else 0)
df["With_a_pet"] = df.Tags.apply(lambda tag: 1 if "With a pet" in tag else 0)
这里用 in tag 子串匹配而非精确相等,正是为了规避标签顺序、空格不规范的匹配陷阱——只要单元格里出现过短语就记 1。
第三步:保存过滤后的数据集
保存前把已经确认无用的列一并删掉,并以新文件名落盘(Hotel_Reviews_Filtered.csv)。README 中的保存代码:
df.drop(["Review_Total_Negative_Word_Counts", "Review_Total_Positive_Word_Counts", "days_since_review", "Total_Number_of_Reviews_Reviewer_Has_Given"], axis = 1, inplace=True)
# Saving new data file with calculated columns
print("Saving results to Hotel_Reviews_Filtered.csv")
df.to_csv(r'../data/Hotel_Reviews_Filtered.csv', index = False)
从源码看,过滤笔记本 的对应单元格额外把 Review_Date 也放进了丢弃列表,并对整段流程计时——其运行记录显示“Filtering took 23.74 seconds”,即整轮列过滤与重算在参考设备上约 24 秒完成。
第四步:加载过滤数据并做停用词优化
下一节要读取的是上面保存的过滤后数据集,而不是原始数据集(README 用加粗特别强调了这一点)。环境准备代码:
import time
import pandas as pd
import nltk as nltk
from nltk.corpus import stopwords
from nltk.sentiment.vader import SentimentIntensityAnalyzer
nltk.download('vader_lexicon')
# Load the filtered hotel reviews from CSV
df = pd.read_csv('../../data/Hotel_Reviews_Filtered.csv')
# You code will be added here
# Finally remember to save the hotel reviews with new NLP data added
print("Saving results to Hotel_Reviews_NLP.csv")
df.to_csv(r'../data/Hotel_Reviews_NLP.csv', index = False)
为什么要先移除停用词
直接在 Negative_Review 与 Positive_Review 两列上跑情感分析,在参考设备(高性能笔记本、快 CPU)上需要 12~14 分钟,取决于所用情感库——这段时间值得优化。停用词(the、and、of 等不改变句子情感色彩的常用词)是安全的提速点:删掉它们情感分析应该更快,但准确性不降(停用词不影响情感判定,只拖慢速度)。README 给出的量化依据:
- 最长的一条负面评论 395 词,去停用词后为 195 词(篇幅直接减半以上);
- 对 515,000 行、2 个评论列做去停用词,参考设备耗时 3.3 秒(情感分析笔记本 的运行记录为 5.77 秒,实际会因 CPU、内存、SSD 等硬件因素浮动)。
去停用词本身是轻量操作,若它能换回情感分析的大幅提速,就是划算的交易。实现上参考了社区里“不同去停用词方案性能对比”的做法——把停用词表预编译成 set 作为缓存,逐词做 O(1) 成员判断:
from nltk.corpus import stopwords
# Load the hotel reviews from CSV
df = pd.read_csv("../../data/Hotel_Reviews_Filtered.csv")
# Remove stop words - can be slow for a lot of text!
# Ryan Han (ryanxjhan on Kaggle) has a great post measuring performance of different stop words removal approaches
# using the approach that Ryan recommends
start = time.time()
cache = set(stopwords.words("english"))
def remove_stopwords(review):
text = " ".join([word for word in review.split() if word not in cache])
return text
# Remove the stop words from both columns
df.Negative_Review = df.Negative_Review.apply(remove_stopwords)
df.Positive_Review = df.Positive_Review.apply(remove_stopwords)
第五步:用 VADER 计算正负情感列
分析器与三种输入情况
README 说明情感结果的检验方法是与评论者的数值评分对照:如果情感分析认为一条负面评论的情感分是 1(极度正面)、正面评论也是 1,而评论者却给了最低分,那么要么评论文本与评分不匹配,要么分析器没认对情感。README 同时提醒:应预期一部分情感分会完全错,且往往是可解释的,例如极端反讽——“Of course I LOVED sleeping in a room with no heating”(“当然,我太喜欢睡在没暖气的房间里了”)会被判成正面,而人类一读就懂这是反话。
NLTK 提供了多种情感分析器可以替换对比,本课选用 VADER(Hutto, C.J. & Gilbert, E.E. (2014). VADER: A Parsimonious Rule-based Model for Sentiment Analysis of Social Media Text. ICWSM-14)。构造分析器与核心回调函数:
from nltk.sentiment.vader import SentimentIntensityAnalyzer
# Create the vader sentiment analyser (there are others in NLTK you can try too)
vader_sentiment = SentimentIntensityAnalyzer()
# Hutto, C.J. & Gilbert, E.E. (2014). VADER: A Parsimonious Rule-based Model for Sentiment Analysis of Social Media Text. Eighth International Conference on Weblogs and Social Media (ICWSM-14). Ann Arbor, MI, June 2014.
# There are 3 possibilities of input for a review:
# It could be "No Negative", in which case, return 0
# It could be "No Positive", in which case, return 0
# It could be a review, in which case calculate the sentiment
def calc_sentiment(review):
if review == "No Negative" or review == "No Positive":
return 0
return vader_sentiment.polarity_scores(review)["compound"]
要点有两个:其一,数据集中没写内容时该列会被填上字面占位值 No Negative / No Positive,必须先拦截返回 0,否则这些占位字符串会被当成真实文本送进情感分析器;其二,取的是 polarity_scores() 返回字典中的 compound 复合分(约 -1 到 +1 的归一化得分),而非 pos/neu/neg 的原始分。
批量应用、计时与人工抽查
# Add a negative sentiment and positive sentiment column
print("Calculating sentiment columns for both positive and negative reviews")
start = time.time()
df["Negative_Sentiment"] = df.Negative_Review.apply(calc_sentiment)
df["Positive_Sentiment"] = df.Positive_Review.apply(calc_sentiment)
end = time.time()
print("Calculating sentiment took " + str(round(end - start, 2)) + " seconds")
README 给出的耗时约为 120 秒(随机器不同而变化);情感分析笔记本 的实际运行记录是 201.07 秒。计算完成后,可以把两列分别按分数排序、打印首尾行来做人工抽查,验证“分数最低的行读起来确实最负、最高的行确实最正”:
df = df.sort_values(by=["Negative_Sentiment"], ascending=True)
print(df[["Negative_Review", "Negative_Sentiment"]])
df = df.sort_values(by=["Positive_Sentiment"], ascending=True)
print(df[["Positive_Review", "Positive_Sentiment"]])
该 notebook 的实际输出印证了这一点:负面情感最低的分值为 -0.9920(“So bad experience memories I hotel The first…”),而排序两端也出现了典型的“名不副实”案例——例如一条写着 “Wifi terribly slow I speed test network upload…” 的文本被排到了高情感分一侧,直观展示了反讽/否定语境下规则式分析器的误差形态,这正是“情感分会错、但往往可解释”的实例。
收尾:重排列并保存最终数据集
README 指出,正式交付给 Challenge 之前还有两件事:重排列(纯外观性的整理,方便人后续探索)与再次保存。重排后的 18 列顺序为:
# Reorder the columns (This is cosmetic, but to make it easier to explore the data later)
df = df.reindex(["Hotel_Name", "Hotel_Address", "Total_Number_of_Reviews", "Average_Score", "Reviewer_Score", "Negative_Sentiment", "Positive_Sentiment", "Reviewer_Nationality", "Leisure_trip", "Couple", "Solo_traveler", "Business_trip", "Group", "Family_with_young_children", "Family_with_older_children", "With_a_pet", "Negative_Review", "Positive_Review"], axis=1)
print("Saving results to Hotel_Reviews_NLP.csv")
df.to_csv(r"../data/Hotel_Reviews_NLP.csv", index = False)
从列顺序可以看出最终数据集的“信息分层”:先是酒店身份与聚合统计(名称、地址、评论数、自算均分),再是个体评分与两条 VADER 情感分,然后是国籍与 8 个 0/1 标签特征,最后才放两列原始(已去停用词)文本。README 建议的完整运行顺序是:先跑完 过滤笔记本 生成 Hotel_Reviews_Filtered.csv,再运行整个 情感分析笔记本 生成 Hotel_Reviews_NLP.csv。
小结与延伸练习
回顾这一课:起点是一个“有列、有数据,但并非全部可验证、可用”的数据集;经过探索、过滤无用列、把 Tags 转成有用的 0/1 特征列、自算酒店级均值、新增情感列之后,最终得到一份能直接支撑推荐建模的 NLP 增强数据集。README 的结论段也概括了本课能力增量:过滤不可信数据、把标签转成可用特征、自算均值、添加情感列,并顺带学会了不少处理自然语言文本的技巧。
Challenge
README 布置的挑战题:数据集现在已经完成了情感分析,试着用本课程学过的策略(比如聚类 clustering)来发现情感分周围存在的模式。可操作的思路是把 Negative_Sentiment、Positive_Sentiment、Reviewer_Score 与 8 个标签列组合成特征矩阵做无监督分群,再检查各簇对应的城市、国籍与标签组合分布——仓库中 5-Clustering 模块 提供了 K-Means 与可视化的完整教材。
Assignment
课后作业 要求:在学习了用 NLTK 给文本指派情感之后,换一个数据集重复这一流程。由于大概率要做一些数据预处理,需要自建 notebook 并记录你的思考过程。其评分标准(Rubric)如下:
| 维度 | 优秀(Exemplary) | 合格(Adequate) | 待改进(Needs Improvement) |
|---|---|---|---|
| 交付物 | 提交完整的 notebook 与数据集,附带解释清楚的单元,说明情感如何被指派 | notebook 缺少充分的解释 | notebook 存在缺陷 |
本篇涉及的仓库文件
| 文件 | 作用 |
|---|---|
| 6-NLP/5-Hotel-Reviews-2/README.md | 本课主体:过滤、标签处理、情感分析全流程 |
| 6-NLP/5-Hotel-Reviews-2/solution/1-notebook.ipynb | 过滤笔记本:产出 Hotel_Reviews_Filtered.csv |
| 6-NLP/5-Hotel-Reviews-2/solution/2-notebook.ipynb | 标签探索笔记本:melt + 正则过滤得到 9 个有效标签 |
| 6-NLP/5-Hotel-Reviews-2/solution/3-notebook.ipynb | 情感分析笔记本:去停用词 + VADER,产出 Hotel_Reviews_NLP.csv |
| 6-NLP/4-Hotel-Reviews-1/README.md | 上一课:数据集列定义与探索式分析 |
| 6-NLP/5-Hotel-Reviews-2/assignment.md | 课后作业:换数据集复现情感分析流程 |
| 6-NLP/data/README.md | 数据目录说明:需将数据集下载到该文件夹 |
适用前提与限制说明:文中所有耗时数据(23.74 秒过滤、3.3/5.77 秒去停用词、约 120~201 秒情感分析)均为课程作者参考设备上的实测记录,随硬件与所用情感库不同会明显浮动;数据集中 Hotel_Reviews.csv 等文件需自行下载放入 6-NLP/data/,仓库本身不包含该 230 MB 原始数据。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00