
简介本资源是一份面向人工智能初学者与数据科学实践者的CSDN平台用户画像构建项目源码聚焦于如何利用机器学习与文本分析技术从行为日志中提炼用户兴趣、偏好与潜在需求解决个性化推荐、精准运营等典型业务问题。压缩包共17个文件含9个核心Python脚本如preprocess.py、train_word2vec.py、seg_data.py等覆盖数据清洗、分词、词向量训练、聚类建模全流程、3个IDE配置XML文件、2个说明类TXT文档及辅助模块整体仅17KB轻量易读便于快速理解用户画像工程链路。已有111人下载学习适合希望掌握真实场景下特征工程、无监督聚类与用户建模落地能力的开发者。资源提供完整可运行代码结构包含预处理—向量化—训练—画像生成的闭环逻辑并附带readme与说明文档帮助读者厘清各模块职责与调用关系是理解AI驱动用户洞察的优质入门实践样本。 作为一个常年泡在CSDN上的人我每天都要刷大量技术帖看着不同用户发的文章、留的评论、收藏的代码脑子里一直有个念头如果把这些零散的行为信息汇聚起来还原成一个活生生的“技术人画像”会是什么样子正好前阵子接手了一个人工智能项目实践的课题我干脆选了“CSDN用户画像”作为切入点自己动手从数据采集、特征构造到建模分群完整做了一套带源码的画像系统。这篇博文就是把整个项目的思路、代码和踩坑记录整理出来希望能给正在折腾用户画像、推荐系统或者人工智能大作业的朋友一些可以直接抄作业的参考。用户画像不是玄学它本质上就是一套“从用户行为里提炼标签再用标签去描述用户”的技术方案。放到CSDN这个场景里用户的阅读、点赞、收藏、评论、发文章、关注技术领域这些行为背后都藏着一个个标签技术水平、技术方向、活跃程度、内容偏好、社区影响力。把这些标签组织起来就能形成一个相对完整的用户视图。这篇文章适合对推荐系统、数据挖掘、人工智能应用感兴趣的同学也适合那些正在找人工智能训练师实操题、大作业选题的开发者我尽量把每一步都讲透代码也直接贴出来。整个项目的核心关键词就三个人工智能、用户画像、源码我会围绕这三个词把整个流程串起来。1. 用户画像整体设计思路与方案选型1.1 从CSDN用户场景看画像的本质很多人对用户画像有个误解觉得画像就是给用户打几个标签比如“Java工程师”“刚入门”“喜欢算法”就完事了。真正落到工程上比这个复杂得多。CSDN的用户群体非常典型有刚入行的大学生有工作三五年的后端工程师有专注前端可视化的大佬也有偶尔搜个问题就不说话的路人。不同用户的行为模式差异极大画像系统要做的就是通过这些行为痕迹反推出“这个人是干什么的、处于什么阶段、喜欢什么内容、有多大概率持续活跃”。在CSDN场景下我会把画像拆成四个维度身份属性、内容偏好、行为活跃、影响力价值。身份属性很好理解就是用户在个人主页填写的职业、技能标签、当前公司、城市这些基础信息。内容偏好则是用户阅读、点赞、收藏、评论的文章里隐含的技术方向偏好。行为活跃反映的是用户最近有没有在平台出现、发了多少帖、回复有多勤快。影响力价值就是粉丝数、文章被阅读量、收藏数这些体现用户在社区里“话语权”的指标。你可能要问了为什么不直接把所有字段都扔给模型让模型自己学是可以但这就引出一个关键问题缺少可解释性。用深度学习硬学用户原始行为序列模型是能出结果但你看不到“这个用户为什么被归为这一类”运营和产品也没法根据画像去做精细化的动作。所以我的思路是先用标签体系把原始数据转化成可解释的特征再基于特征做聚类分群和偏好预测中间每层都有明确含义出了问题也容易排查。1.2 标签体系与整体架构标签体系我采用了业内常用的“事实标签—规则标签—模型标签”三层结构。最底层是事实标签也就是从数据源直接提取出来的客观信息比如用户ID、注册时间、文章数、粉丝数、最近登录时间。中间层是规则标签基于明确的业务规则和统计口径生成比如“连续活跃7天”“高频发布者”“Java文章阅读占比超过60%”这类。最上层是模型标签需要借助机器学习算法来推断例如通过聚类算法得到“高质量内容创作者”群体或用文本相似度计算把用户归入某个技术方向。这种分层设计最大的好处是灵活规则标签可以随时根据业务调整模型标签依赖的特征又比较稳定二者互不干扰。整体架构上我按这条链路走数据采集模块对接CSDN公开信息和爬虫→ 数据预处理模块清洗、去重、标准化→ 特征工程模块计算行为指标和偏好向量→ 算法建模模块聚类、关键词提取、影响力评分→ 结果存储模块把画像标签写进数据库→ 可视化展示模块用图表让画像“看得见”。每一个模块在工程上都是独立的换一个数据源或者换一套可视化方案不会牵连到其他部分。技术栈方面我全部选了Python生态核心依赖是pandas、scikit-learn、jieba、wordcloud和matplotlib。为什么不用Spark或者Flink那套大数据组件因为CSDN单用户的数据量级做原型验证其实不大万级用户、百万级行为记录纯Python完全扛得住而且迭代起来非常方便。等原型跑通了、算法验证有效了再平移到大数据的分布式框架上也不迟。2. 数据采集与预处理画像系统的地基2.1 数据采集的合规姿势说到数据采集必须强调一点做技术实践一定要遵守平台的用户协议和Robots协议不要用过度的爬虫手段给目标站点造成压力。CSDN本身的部分数据通过开放接口或者公开页面就能拿到。我自己的数据集分成了两块。第一块是CSDN用户个人主页里的公开信息包括昵称、城市、职业、个人简介、粉丝数、获赞数、评论数、访问量、文章数等。第二块是用户发布的技术文章信息包括标题、标签、分类、发布时间、阅读数、点赞数、评论数、收藏数。如果你是做大作业或者练习不用真的去抓海量真实数据完全可以构造一份模拟数据来跑通流程。我项目里用的数据就是混合的一部分真实公开数据做验证一部分按CSDN平台的统计特征模拟出来。这么做的好处是练的时候不用考虑采集频控和封禁问题等流程全部跑通后再决定是否引入更多真实数据。模拟数据的构造方法也很简单用numpy的随机函数按指定分布生成用户ID、行为时间、文章分类这些字段即可关键是分布要尽量贴合真实平台的结构。为了体验完整的工程流程我还是写了一个轻量级的数据采集模块。我的代码里用了requests加BeautifulSoup去解析个人主页的公开数据同时在请求之间加随机延时控制请求频率避免对目标服务器造成压力。如果你只是想练画像算法我建议直接跳过这一步用下面几条命令先造一份CSV数据出来把精力放在特征工程和模型这层。import pandas as pd import numpy as np # 构造模拟数据5000个CSDN用户行为字段贴近真实平台结构 np.random.seed(42) user_ids [fuser_{i} for i in range(5000)] register_days np.random.randint(30, 3650, size5000) # 注册天数 article_count np.random.pareto(2, size5000).astype(int) 1 # 文章数偏幂律分布 fans_count np.random.pareto(1.5, size5000).astype(int) # 粉丝数偏幂律分布 read_total np.random.randint(0, 100000, size5000) # 总访问量 like_total np.random.randint(0, 5000, size5000) # 总获赞数 last_active_days_ago np.random.randint(0, 180, size5000) # 最近活跃距今天数 skills_pool [Java, Python, 前端, 算法, 数据库, 运维, 移动端, 人工智能] skills np.random.choice(skills_pool, size5000) df_user pd.DataFrame({ user_id: user_ids, register_days: register_days, article_count: article_count, fans_count: fans_count, read_total: read_total, like_total: like_total, last_active_days_ago: last_active_days_ago, main_skill: skills }) df_user.to_csv(csdn_user_base.csv, indexFalse, encodingutf-8-sig) print(df_user.head())2.2 数据清洗与标准化数据拿到手以后千万别急着上特征工程先做清洗。我在这个环节踩过不少坑最典型的就是字段类型不一致和缺失值。比如粉丝数在网页里可能是“1.2万”这种带单位的字符串直接拿来做计算肯定报错。收藏数、阅读数这些字段同样有这个问题。我的处理方式是把所有“万”“亿”等单位统一转成数值型再统一按整数处理。def parse_count(value): if isinstance(value, str): value value.strip() if 万 in value: return int(float(value.replace(万, )) * 10000) if 亿 in value: return int(float(value.replace(亿, )) * 100000000) try: return int(value) except: return 0 return int(value)除了单位问题还有几个常规清洗动作剔除user_id重复的记录注册时间为空的用户直接删除文章数、粉丝数出现负数或异常巨大值的做截断处理活跃时间字段统一成时间戳格式。清洗的目标只有一个让数据在进入特征工程之前是一张干净、标准、没有明显脏值的二维表。我习惯把所有清洗逻辑写成一个函数方便后续接入新数据时一键复用。标准化这块我建议对偏态分布明显的字段做对数变换。粉丝数、文章数、访问量这几列在平台里基本都是头尾差距极大的长尾分布少数大佬占据大部分流量直接用原始值喂给模型聚类结果会被那几十个大V彻底带偏。取对数之后数据分布更接近正态模型效果会提升不少。import numpy as np df pd.read_csv(csdn_user_base.csv) df[fans_log] np.log1p(df[fans_count]) df[read_log] np.log1p(df[read_total]) df[like_log] np.log1p(df[like_total])3. 特征体系构建把原始行为变成可计算的指标3.1 基础特征平台统计指标基础特征就是那些能直接算出来的数字包括用户的注册天数、文章数、粉丝数、总访问量、总获赞数、获评数、最近活跃间隔、关注数等。这些特征单独看意义有限但组合起来就能反映很多事情。举个例子注册天数除以文章数能得出平均多久发一篇文章这是“创作持续性”的直观指标点赞总数除以文章数能得出单篇文章的平均获赞量这直接反映内容质量。我在实践里还会构造一批衍生特征。比如粉丝数与文章数的比值可以粗略判断用户是偏“内容型”还是偏“社交型”——如果文章不多但粉丝很多说明这个人可能是行业里有影响力的大佬质量远胜数量。再比如最近活跃间隔这个字段如果长期不活跃那即使粉丝再多对平台来说也是一个“沉睡用户”运营上需要唤醒策略推荐权重也会下调。下面是我的基础特征计算代码。# 构造基础特征 df_feature pd.DataFrame() df_feature[user_id] df[user_id] df_feature[register_days] df[register_days] df_feature[article_count] df[article_count] df_feature[avg_articles_per_month] df[article_count] / (df[register_days] / 30) df_feature[avg_read_per_article] df[read_total] / (df[article_count] 1) df_feature[avg_like_per_article] df[like_total] / (df[article_count] 1) df_feature[fans_per_article] df[fans_count] / (df[article_count] 1) df_feature[active_recency_days] df[last_active_days_ago]3.2 行为偏好特征从内容标签看用户兴趣CSDN用户的核心行为就是看文章和写文章所以内容层面的偏好特征在画像里占的比重非常大。我做的思路是把一篇技术文章看成由“分类标签关键词”组成的语义包通过用户对文章的阅读、点赞、收藏、评论等行为把语义包“转移”到用户身上形成用户的内容偏好向量。具体实现就是对文章打上技术方向标签比如“Java”“Python”“算法”“前端”然后按行为权重累加用户在各个方向上的得分。这里有个细节要注意不同类型的行为代表不同的用户意图。阅读很可能是随便点开看看点赞表示认可收藏表示想以后查阅评论则有更强的主观表达意愿。因此我分配的行为权重是阅读1、点赞2、收藏3、评论4。这样算出来的偏好向量能更好地区分“随手看”和“深度关注”。如果采集到的数据里只有文章没有用户阅读记录也可以用用户自己发布的文章标签来近似准确率会低一些但方向没错。# 示例根据用户阅读过的文章标题和标签统计各技术方向得分 import jieba from collections import defaultdict def compute_interest_vector(user_articles): user_articles : list of dict, each item contains title, labels, action_type action_type : read1, like2, collect3, comment4 scores defaultdict(float) action_weight {read: 1, like: 2, collect: 3, comment: 4} for article in user_articles: weight action_weight.get(article[action_type], 1) for label in article[labels]: scores[label] weight # 对标题分词叠加关键词权重简化只取词频最高的词 words jieba.lcut(article[title]) for word in words[:5]: scores[word] 0.5 * weight total sum(scores.values()) if total 0: return {k: v / total for k, v in scores.items()} return {}3.3 用户价值分层内容平台版RFM模型电商领域有个经典的用户分层模型叫RFM分别代表最近一次消费时间、消费频率、消费金额。我在给CSDN做画像的时候借鉴了这个思路把它改造成了内容平台的版本R代表最近一次活跃间隔越小越好、F代表发文章或者评论的频率、M代表内容贡献的总量和互动深度可以合并阅读量、点赞量、收藏量这些指标。把这三个维度分别做分位数切分就能把用户粗分成几个价值层次。这个层级的划分直接服务于后面的业务应用。比如高R高F高M的用户是核心创作者平台要重点维护高R低F低M的用户是活跃潜水者适合用推送触达低R高F高M的用户曾经很活跃但正在流失需要用挽留策略。这个分层结果作为基础特征喂给聚类算法也能帮助模型更快捕捉到用户群体之间的差异。df_feature[recency_score] pd.qcut(df_feature[active_recency_days], 4, labels[4, 3, 2, 1]) df_feature[freq_score] pd.qcut(df_feature[article_count], 4, labels[1, 2, 3, 4]) df_feature[value_score] pd.qcut(df_feature[avg_like_per_article], 4, labels[1, 2, 3, 4]) df_feature[rfm_score] ( df_feature[recency_score].astype(int) df_feature[freq_score].astype(int) df_feature[value_score].astype(int) )4. 核心算法与源码实现4.1 关键词提取与用户内容偏好识别用户画像里最重要的一块是从文章和标题中提取出能代表用户技术方向的关键词。这一步我选择了TF-IDF算法而不是用复杂的大模型。原因是TF-IDF实现简单、可解释性强而且对CSDN这种技术文章主题相对聚焦的场景效果已经足够了。TF-IDF的核心思想是如果一个词在一篇文章里出现得很频繁但在整个语料库的其他文章里出现得很少那这个词对这篇文章就有很强的区分度也就是“这篇文章在讲什么”。实现的时候分两步。第一步对所有用户的文章标题和内容标签做分词清洗掉停用词和标点第二步用scikit-learn的TfidfVectorizer对分词后的文本做向量化然后针对每个用户把他名下所有文章的TF-IDF向量叠加取出权重最高的Top关键词作为这个用户的内容标签。在CSDN场景里Top关键词通常就是Python、Java、Kafka、算法、MySQL这些词非常直观。from sklearn.feature_extraction.text import TfidfVectorizer documents df[article_title].fillna().tolist() # 每个用户或每篇文章的标题文本 tfidf TfidfVectorizer(tokenizerjieba.lcut, max_features5000, stop_words[的, 了, 是, 在, 我]) tfidf_matrix tfidf.fit_transform(documents) # 取出每个用户的Top关键词 def get_top_keywords(user_index, top_n5): scores tfidf_matrix[user_index].toarray()[0] top_indices scores.argsort()[-top_n:][::-1] feature_names tfidf.get_feature_names_out() return [feature_names[i] for i in top_indices if scores[i] 0]4.2 用户分群KMeans聚类剖析相似群体画像的另一个核心任务是分群也就是把平台上特征相近的用户聚到一起形成几个有明确特征的群体。我用了KMeans聚类它属于无监督学习算法不需要标注好的标签算法自动根据特征向量之间的距离把样本分到K个簇。在用户画像场景里KMeans的表现中规中矩但只要特征工程做扎实它出来的群组解释性非常好每个簇都能对应一类典型用户。确定K值的时候我尝试了两种方法。第一种是肘部法则画出不同K值对应的簇内误差平方和SSE看曲线在哪个位置出现拐点拐点处的K值就是相对合理的聚类数。第二种是业务判断我预期CSDN用户大概能分成4到6个比较清晰的群体所以K5是首选聚出来的类分别是初级学习者、活跃创作者、领域专家、普通浏览者、沉睡用户。实践下来K5的群体边界比较清晰业务上也说得通所以最终选了5。from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler # 选择参与聚类的特征列 feature_cols [fans_log, read_log, like_log, avg_articles_per_month, avg_like_per_article, rfm_score] X df_feature[feature_cols].fillna(0).values X_scaled StandardScaler().fit_transform(X) kmeans KMeans(n_clusters5, random_state42, n_init10) df_feature[cluster] kmeans.fit_predict(X_scaled) # 查看每个簇的特征均值判断群体含义 print(df_feature.groupby(cluster)[feature_cols].mean())4.3 活跃度分层与用户生命周期状态识别最后一个建模环节是给每个用户打上“生命周期状态”标签我把状态分为活跃期、沉默期、流失期、回归期和新人期。这个标签的逻辑跟业务强相关新人期是注册时间短且近期有活跃行为活跃期是最近7天内活跃且发文或评论频率高沉默期是最近30天内没有行为但之前活跃过流失期是超过90天没有活跃回归期是曾经沉默但最近重新活跃。这个状态识别本质上就是根据时间窗口做规则判断简单直接不需要复杂的机器学习模型。我把这个状态跟用户价值分层组合起来形成一个二维表格这样就能做很多精细化的运营动作。比如“高价值流失期”的用户平台可以考虑发激活邮件“中价值活跃期”的用户是内容消费的主力推荐系统的权重可以适当提高“新人期”用户则需要更多引导和鼓励比如推荐热门问题或零基础入门文章。def lifecycle_status(row): if row[register_days] 90 and row[active_recency_days] 7: return 新人期 elif row[active_recency_days] 7: return 活跃期 elif row[active_recency_days] 30: return 沉默期 elif row[active_recency_days] 90: return 回归期 else: return 流失期 df_feature[lifecycle] df_feature.apply(lifecycle_status, axis1)5. 画像可视化与业务应用5.1 画像标签的可视化展示画像建完了不能只停留在数据库表里得让人看得见、看得懂。我用Python的matplotlib和wordcloud做了几个基础的可视化用户群体分布饼图、不同簇的特征对比柱状图、用户关键词词云。这些图放在项目汇报的PPT里非常直观评审老师或者老板一眼就能看懂你的画像系统产出了什么。词云这块我是把所有用户的Top关键词汇总后生成一张整体的技术热词图再用不同簇分别生成子词云就能看出不同用户群体关注的技术方向差异。比如“初级学习者”群体的词云里集中出现“入门”“教程”“环境搭建”“领域专家”群体则更多出现“分布式”“源码”“架构设计”这些深度词汇。这种可解释性正是画像系统要追求的效果。from wordcloud import WordCloud import matplotlib.pyplot as plt # 汇总所有用户关键词 all_keywords .join(df_feature[top_keywords].dropna().tolist()) wordcloud WordCloud(font_pathmsyh.ttc, width800, height400, background_colorwhite).generate(all_keywords) plt.figure(figsize(10, 5)) plt.imshow(wordcloud, interpolationbilinear) plt.axis(off) plt.show()5.2 画像的典型业务应用场景一个落地的画像系统至少能在三个地方发挥作用。第一个是内容推荐这个最容易理解根据用户的偏好向量从文章库里找出跟用户历史感兴趣的技术方向高度相似的文章按相似度排序推荐给用户。第二个是搜索排序优化当两个用户搜索同一个关键词时搜索结果可以根据画像做微调擅长后端的用户优先看到后端视角的回答刚入门的新手则优先看到基础教程。第三个是运营活动圈选通过生命周期状态和价值分层这两个维度圈出特定人群做针对性的活动比如针对“流失期高价值用户”推送回归奖励针对“活跃期中价值用户”邀请参加创作打卡。内容推荐的代码可以直接复用前面算出来的偏好向量。我用余弦相似度计算用户和文章向量的相似度推荐TopN篇文章。因为偏好向量已经在标签层结构化过了这部分代码写起来很快实测效果也还可观。from sklearn.metrics.pairwise import cosine_similarity # user_vector: 某个用户的偏好向量1 x n # article_matrix: 候选文章向量m x n # 返回相似度最高的TopN文章索引 def recommend_articles(user_vector, article_matrix, top_n10): sim_scores cosine_similarity(user_vector, article_matrix)[0] top_indices sim_scores.argsort()[-top_n:][::-1] return top_indices, sim_scores[top_indices]6. 常见问题与排查技巧实录6.1 数据稀疏问题用户行为不够怎么办我遇到的第一个问题是用户行为数据稀疏。很多用户只看过一篇文章或者一篇文章都没发过这种情况下偏好向量几乎全是零聚类和推荐都会失效。一个目前比较实用的办法是“协同填充”根据用户的基础属性注册天数、主技能、访问总次数找相似用户把相似用户的偏好向量加权平均后补充到当前用户的向量里。另外一个办法是降维把偏好向量从几千维降到几十维用隐含语义去表达用户偏好这样即使原始行为很稀疏降维后的向量也能捕捉到部分信息。6.2 冷启动问题新用户画像空白新注册用户没有行为记录画像基本是一片空白。我的处理方式是先给一个“默认画像”统一归入“新人期基础用户”分组推荐策略上优先推热门内容和入门教程。等到用户产生了几次有效行为比如连续点击三篇文章再触发画像更新用实时流处理的方式把新行为叠加到偏好向量上。在原型系统里我简化成每6小时批量更新一次新增用户的画像这样延迟还能接受。6.3 画像漂移用户兴趣会变用户的技术方向不是一成不变的。一个程序员可能今年专注PHP明年转做Go后年又研究AI框架。如果画像只靠历史数据累计就会产生严重的滞后。我的应对策略是给行为数据加时间衰减权重越近的行为对画像的影响越大。具体实现就是用指数衰减函数比如半衰期设为90天超过180天的旧行为权重就很低了。这样用户的画像能比较快地跟随兴趣变化对推荐和搜索的实时性要求也能响应得更及时。6.4 画像效果怎么评估画像系统不是上线就完事了得知道它到底准不准。我的评估思路分三层。第一层是特征覆盖率算一下画像中非空标签的比例如果大量用户画像稀疏说明采集或特征工程环节有问题。第二层是聚类结果稳定性同一批数据跑多次KMeans看聚类中心是否稳定如果每次结果差异很大就要考虑增加聚类次数或者调整特征。第三层是业务效果指标画像驱动推荐之后对比画像上线前后的文章点击率、收藏率和用户停留时长这个指标最能说明画像系统有没有真正创造价值。写在最后从数据采集到标签体系设计从特征工程到聚类建模再到可视化展示这个CSDN用户画像项目走下来我最大的感受是用户画像系统的核心不在算法有多深奥而在特征工程和业务理解的深度。TF-IDF、KMeans、余弦相似度都是非常成熟的基础方法但把它们组合成一个合理的链路每一环都要根据场景反复调优这才是实际开发中真正花时间的地方。如果你想用这个项目做人工智能大作业或者想学习源码实现我建议你拿到代码之后先不改任何参数跑一遍最基础的流程把每一步的中间结果打印出来看一遍然后再去调整聚类数、关键词数量、时间衰减系数这些参数你会发现每一步改动带来的变化都清晰可见。这个项目后续还能继续扩展的方向很多比如接入实时数据流、用深度模型做序列化行为建模或者把推荐模块单独抽成一个在线服务都可以让它变成一套完整的工程系统。本文还有配套的精品资源点击获取