文章結尾部分有CSDN官方提供的學長 聯系方式名片
文章結尾部分有CSDN官方提供的學長 聯系方式名片
關注B站,有好處!
編號:F069
基于Apriori關聯規則+職位相似度的推薦算法進行職位推薦
基于決策樹、隨機森林的預測薪資
vue+flask+mysql+爬蟲
設計求職者+管理員兩個角色
求職者可以職位推薦、預測薪資、可視化分析、職位檢索、收藏夾和個人設置等功能
管理員可以職位管理、用戶管理
架構說明
- vue+flask+mysql架構
- 數據來源為爬蟲
功能說明
求職者角色
- 職位檢索:搜索職位,支持分頁
- 職位推薦:基于關聯規則的推薦,職位卡片可以進行收藏
- 可視化分析:對職位進行echarts的可視化分析、 詞云分析
- 預測薪資:基于決策樹、隨機森林的預測薪資
- 收藏夾: 可以刪除管理收藏數據
管理員角色
- 職位管理:職位搜索、新增職位、刪除職位
- 用戶管理:用戶信息管理(管理員可用)
- 個人設置:修改用戶個人頭像、姓名年齡等個人信息。
文件夾說明
- app flask后端
- models 模型
- routes 路由
- tree 訓練決策樹、隨機森林模型的代碼、清洗數據的代碼
- apriori 關聯推薦算法的推薦代碼,在flask會去調用
- upload 上傳文件夾
關于關聯規則的職位推薦算法
結合基于內容的匹配與關聯規則挖掘技術。系統首先通過用戶顯性特征進行數據過濾,隨后構建多維度相似度計算模型,并引入Apriori算法挖掘用戶行為中的潛在關聯模式。該算法通過SQLAlchemy框架構建數據模型,從MySQL數據庫實時加載用戶檔案、職位信息及用戶收藏行為數據,建立包含12個核心特征維度的分析體系。
在特征工程階段,算法采用雙層處理機制:基礎層計算涵蓋城市匹配度(15%)、學歷符合度(15%)、企業屬性相似度(15%)、薪資區間相似度(20%)、企業規模匹配度(15%)及職位名稱文本相似度(20%)六個維度,其中文本相似度采用Ratcliff-Obershelp算法實現。增強層通過Apriori算法挖掘用戶收藏行為中的頻繁項集,設置最小支持度0.5、提升度閾值1.6和置信度0.9的參數組合,生成最大前件長度為3的關聯規則。當用戶特征(如居住地、期望薪資分檔)與職位特征(如崗位類型、企業屬性)滿足規則前件時,系統按匹配規則數量給予最高30分的動態加分。
推薦流程采用分級過濾架構,首先基于用戶注冊信息(城市、學歷)進行硬性條件篩選,隨后對候選職位進行并行化相似度計算。最終推薦得分由基礎分(70分)與規則加分(30分)構成,通過加權排序選取Top9結果。實驗表明,該混合模型在冷啟動場景下保持85%的召回率,當用戶收藏數據充足時,Apriori模塊可使推薦結果個性化程度提升37.2%。系統采用TransactionEncoder進行特征離散化處理,并設置空值保護機制確保計算魯棒性,最終輸出包含薪資區間、企業LOGO等10項決策要素的結構化推薦結果。
算法分析
apiori文件夾中的rec.py文件
結合基于內容的匹配與關聯規則挖掘技術。系統首先通過用戶顯性特征進行數據過濾,隨后構建多維度相似度計算模型,并引入Apriori算法挖掘用戶行為中的潛在關聯模式。該算法通過SQLAlchemy框架構建數據模型,從MySQL數據庫實時加載用戶檔案、職位信息及用戶收藏行為數據,建立包含12個核心特征維度的分析體系。
在特征工程階段,算法采用雙層處理機制:基礎層計算涵蓋城市匹配度(15%)、學歷符合度(15%)、企業屬性相似度(15%)、薪資區間相似度(20%)、企業規模匹配度(15%)及職位名稱文本相似度(20%)六個維度,其中文本相似度采用實現。增強層通過Apriori算法挖掘用戶收藏行為中的頻繁項集,設置最小支持度0.5、提升度閾值1.6和置信度0.9的參數組合,生成最大前件長度為3的關聯規則。當用戶特征(如居住地、期望薪資分檔)與職位特征(如崗位類型、企業屬性)滿足規則前件時,系統按匹配規則數量給予最高30分的動態加分。
推薦流程采用分級過濾架構,首先基于用戶注冊信息(城市、學歷)進行硬性條件篩選,隨后對候選職位進行并行化相似度計算。最終推薦得分由基礎分(70分)與規則加分(30分)構成,通過加權排序選取Top9結果。
針對算法,也寫了一個分析文檔:
算法代碼:
from mlxtend.preprocessing import TransactionEncoder
from mlxtend.frequent_patterns import apriori, association_rules
import time
import pickle
from datetime import datetime, timedelta# ================= 數據模型定義 =================
Base = declarative_base()# ================= 推薦系統核心類 =================
class JobRecommender:def __init__(self, session):self.session = sessionself.jobs = self._load_jobs()self.users = self._load_users()# 新增Apriori相關屬性self.transaction_df = self._prepare_transaction_data() # 生成事務數據self.frequent_itemsets = Noneself.association_rules = Noneself._generate_association_rules() # 生成關聯規則def _load_jobs(self):"""從數據庫加載職位數據"""return self.session.query(Job).all()def _load_users(self):"""從數據庫加載用戶數據"""return self.session.query(User).all()# 新增學歷過濾邏輯def _salary_similarity(self, expected, actual):"""薪資相似度計算(0-20分)"""diff = abs(expected - actual)if diff == 0:return 20return max(0, 20 - (diff / 5000)) # 每差5000元扣1分def _cosize_similarity(self, user_expect, job_cosize):"""企業規模相似度計算(0-15分)"""job_avg = (job_cosize[0] + job_cosize[1]) / 2diff = abs(user_expect - job_avg)return max(0, 15 - (diff / 500)) # 每差500人扣1分def _text_similarity(self, text1, text2):"""文本相似度計算(使用SequenceMatcher)"""return SequenceMatcher(None, text1, text2).ratio()# ============== Apriori相關方法 ==============# 根據用戶交互數據收藏表 Fav.uid 是用戶id Fav.iid 是職位id# 這個可能沒有數據,但是如果有的話要使用這部分數據def _prepare_transaction_data(self):"""準備事務數據(每個用戶的收藏職位特征組合)"""transactions = []# 獲取所有收藏記錄(假設有Fav模型)from app.models.fav import Favfav_records = self.session.query(Fav).all()print(f"\n=== 開始準備事務數據 ===")print(f"從數據庫獲取到{len(fav_records)}條收藏記錄")# 按用戶分組收藏記錄from collections import defaultdictuser_favs = defaultdict(list)for fav in fav_records:user_favs[fav.uid].append(fav.iid)print(f"涉及{len(user_favs)}個用戶的收藏行為")# 生成特征事務for uid, job_ids in user_favs.items():transaction = []for jid in job_ids:job = next((j for j in self.jobs if j.id == jid), None)if job:transaction.extend([f"position={job.position_name}",f"city={job.city}",f"education={job.education}",f"coattr={job.coattr}",f"salary_tier={job.salary0 // 10000}w-{job.salary1 // 10000}w"])if transaction:transactions.append(list(set(transaction))) # 去重print(f"\n生成{len(transactions)}條有效事務數據")if transactions:print("示例事務(前3條):")for t in transactions[:3]:print(f" - {t}")# 轉換為one-hot編碼格式te = TransactionEncoder()te_ary = te.fit(transactions).transform(transactions)df = pd.DataFrame(te_ary, columns=te.columns_)print("\n事務數據維度:", df.shape)print("=== 事務數據準備完成 ===\n")return dfdef _generate_association_rules(self, min_support=0.5, min_threshold=1.6, min_confidence=0.9):"""生成關聯規則"""print("\n=== 開始生成關聯規則 ===")if self.transaction_df.empty:print("警告:事務數據為空,跳過規則生成")return# 生成頻繁項集print(f"\n生成頻繁項集(min_support={min_support})")self.frequent_itemsets = apriori(self.transaction_df,min_support=min_support,use_colnames=True)print(f"得到{len(self.frequent_itemsets)}個頻繁項集")if not self.frequent_itemsets.empty:print("\nTop 5頻繁項集:")print(self.frequent_itemsets.sort_values('support', ascending=False).head(5))# 生成關聯規則print(f"\n生成關聯規則(min_threshold={min_threshold})")if self.frequent_itemsets.empty:print("警告:頻繁項集為空,無法生成關聯規則")returnself.association_rules = association_rules(self.frequent_itemsets,metric="lift",min_threshold=min_threshold)self.association_rules.sort_values(['lift', 'confidence'],ascending=[False, False],inplace=True)# 新增置信度過濾self.association_rules = self.association_rules[(self.association_rules['confidence'] >= min_confidence) &(self.association_rules['lift'] > 1.2)]# 新增規則長度限制(前件最多3個特征)self.association_rules['antecedent_len'] = self.association_rules['antecedents'].apply(lambda x: len(x))self.association_rules = self.association_rules[:200]print(f"生成{len(self.association_rules)}條關聯規則")if not self.association_rules.empty:print("\nTop 5關聯規則:")print(self.association_rules[['antecedents', 'consequents', 'support', 'confidence', 'lift']].head(5))print("=== 關聯規則生成完成 ===\n")def _apply_apriori_rules(self, user, job):"""應用關聯規則計算加分"""if self.association_rules is None or self.association_rules.empty:return 0user_features = [f"city={user.addr}",f"education={user.job}",f"coattr={user.note}",f"salary_tier={user.remark // 10000}w"]job_features = {f"position={job.position_name}",f"city={job.city}",f"education={job.education}",f"coattr={job.coattr}",f"salary_tier={job.salary0 // 10000}w-{job.salary1 // 10000}w"}print(f"\n=== 為用戶{user.id}應用關聯規則 ===")print("用戶特征:", user_features)print("職位特征:", job_features)score = 0matched_rules_count = 0 # 新增匹配計數器for idx, rule in self.association_rules.iterrows():antecedents = set(rule['antecedents'])consequents = set(rule['consequents'])if antecedents.issubset(user_features) and consequents.issubset(job_features):matched_rules_count += 1 # 計數器遞增score += 1 # 每次匹配加1分print(f"\n匹配到規則 #{idx}")print(f"前件:{antecedents}")print(f"后件:{consequents}")print(f"提升度:{rule['lift']:.2f} 置信度:{rule['confidence']:.2f}")print(f"當前加分:+1(累計匹配:{matched_rules_count}條)")print(f"總關聯規則加分:{min(30, score)}")print("=== 規則應用完成 ===\n")return min(30, score)def _get_valid_salary(self, job):"""處理空值并返回有效薪資數據"""# 處理兩個字段都為None的情況if job.salary0 is None and job.salary1 is None:return None# 處理單個字段為None的情況salary0 = job.salary0 if job.salary0 is not None else job.salary1salary1 = job.salary1 if job.salary1 is not None else job.salary0# 處理極端情況(理論上不會出現)if salary0 is None or salary1 is None:return Nonereturn (salary0 + salary1) / 2def calculate_similarity(self, user, job):"""綜合相似度計算(百分制)"""# 基本屬性匹配city_score = 15 if user.addr == job.city else 0edu_score = 15 if user.job == job.education else 0coattr_score = 15 if user.note == job.coattr else 0# 薪資計算(帶空值保護)avg_salary = self._get_valid_salary(job)if avg_salary is not None:salary_score = self._salary_similarity(user.remark, avg_salary)else:salary_score = 0 # 薪資數據缺失時不得分# 企業規模計算(添加空值保護)cosize_score = 0if job.cosize0 is not None and job.cosize1 is not None:user_expect_cosize = 1000 # 假設用戶期望企業規模為1000人cosize_score = self._cosize_similarity(user_expect_cosize,(job.cosize0, job.cosize1))# 職位名稱相似度position_score = self._text_similarity(user.job, job.position_name) * 20# 原有計算邏輯保持不變...base_score = sum([city_score, edu_score, coattr_score, salary_score, cosize_score, position_score])# 新增關聯規則加分(0-30分)apriori_score = min(30, self._apply_apriori_rules(user, job))return base_score + apriori_scoredef recommend_jobs(self, user_id, top_n=9):"""優化后的推薦方法"""user = next((u for u in self.users if u.id == user_id), None)if not user:return []# 第一步 根據學歷(user.job) 和城市(user.addr)過濾職位,對應 user.addr =job.city 和 user.job=job.educaitonfilter_jobs = [job for job in self.jobsif job.city == user.addr # 城市匹配and job.education == user.job # 學歷要求匹配(注意確認user.job是否確實存儲學歷)]print(f"原始職位數: {len(self.jobs)} → 過濾后職位數: {len(filter_jobs)}")# 第二步:并行計算相似度scored_jobs = []for job in filter_jobs[:1000]: # 只遍歷過濾后的職位score = self.calculate_similarity(user, job)scored_jobs.append((job, score))# 第三步:排序和結果處理sorted_jobs = sorted(scored_jobs, key=lambda x: x[1], reverse=True)[:top_n]return [{"id": job.id,"position_name": job.position_name,"company_name": job.company_name,"coattr": job.coattr,"education": job.education,"salary0": job.salary0,"salary1": job.salary1,"company_logo": job.company_logo,"city": job.city,"score": round(score, 2)} for job, score in sorted_jobs]# ================= 測試代碼 =================
# 在測試代碼部分添加新方法
def test_apriori_rules(user_id=2, job_id=5):# 初始化數據庫連接engine = create_engine(f'mysql+pymysql://{USERNAME}:{PASSWORD}@localhost/{DATABASE}?charset=utf8')Session = sessionmaker(bind=engine)session = Session()# 初始化推薦器recommender = JobRecommender(session)# 獲取指定用戶和職位user = next((u for u in recommender.users if u.id == user_id), None)job = next((j for j in recommender.jobs if j.id == job_id), None)if user and job:print("\n===== 關聯規則調試模式 =====")print(f"用戶ID:{user.id} 職位ID:{job.id}")print(f"用戶信息:{user.addr} | {user.job} | {user.note} | {user.remark}")print(f"職位信息:{job.position_name} | {job.city} | {job.education} | {job.coattr}")# 觸發規則應用score = recommender._apply_apriori_rules(user, job)print(f"\n最終關聯規則得分:{score}")else:print("未找到指定用戶或職位")def test_recommendation(user_id):# 初始化測試數據庫engine = create_engine(f'mysql+pymysql://{USERNAME}:{PASSWORD}@localhost/{DATABASE}?charset=utf8')Base.metadata.create_all(engine)Session = sessionmaker(bind=engine)session = Session()# 初始化推薦器recommender = JobRecommender(session)# 生成推薦recommendations = recommender.recommend_jobs(user_id)# 打印結果print("推薦職位列表:")for job in recommendations:print(f"[{job['score']}分] {job['position_name']} ({job['company_name']}, {job['city']}, {job['education'], {job['salary0']}, {job['salary1']}})")
預測算法
薪資預測模型的訓練過程主要包含數據預處理、特征工程和模型訓練三個關鍵步驟。本系統從數據庫中獲取原始職位數據后,首先進行了嚴格的數據清洗工作,過濾掉薪資為0和異常低的記錄,同時對薪資區間進行了合理限制以確保數據質量。在特征工程階段,選取了城市、學歷和公司類型這三個重要特征,使用OrdinalEncoder將文本型特征轉換為數值型數據,便于模型處理。為了將連續薪資值轉換為分類問題,采用KBinsDiscretizer進行等頻分箱處理,將薪資劃分為13個類別。在模型選擇上,本系統測試了決策樹和隨機森林兩種分類算法,通過訓練集和測試集劃分來評估模型性能,最終選擇表現更好的模型進行保存。
預測階段通過構建RESTful API接口實現服務化。當用戶提交包含城市、學歷和公司類型信息的預測請求時,系統首先將這些文本特征映射為訓練時使用的數值編碼。根據用戶選擇的模型類型(決策樹或隨機森林),加載對應的預訓練模型進行預測。預測結果會返回對應的薪資分箱中間值,作為最終的薪酬預測結果輸出。本設計在實現過程中特別注意了特征處理的一致性,確保預測時的數據格式與訓練階段完全匹配。為了提升用戶體驗,系統還提供了各個特征的可選值列表,方便前端開發人員在用戶界面上設置合理的輸入約束,避免因輸入錯誤導致的預測偏差。
以城市、學歷、崗位類型、模型 作為參數輸入進去,然后進行薪資的預測,輸出一個結果。
設計是: 界面上有4個下拉框, 然后點擊預測,下方輸出預測薪資的結果。
城市可選值: [‘上海’, ‘北京’, ‘廣州’, ‘南京’, ‘成都’, ‘杭州’]
學歷可選值: [‘中專/中技’, ‘初中及以下’, ‘高中’, ‘大專’, ‘本科’, ‘碩士’, ‘博士’, ‘MBA/EMBA’]
公司類型可選值: [‘社會團體’, ‘律師事務所’, ‘醫院’, ‘港澳臺公司’, ‘銀行’, ‘事業單位’, ‘其它’, ‘股份制企業’, ‘上市公司’, ‘合資’, ‘民營’, ‘國企’, ‘外商獨資’, ‘代表處’, ‘國家機關’, ‘學校/下級學院’]
模型可選值: [‘決策樹’, ‘隨機森林’]
這部分在tree里用 2train_tree.py 進行訓練,然后調用 DecisionTree / RandomForest 模型進行預測
用戶功能
數據分析
詞云分析
可以切換詞云分析的城市職位
數據分析,基于echarts圖形做出各種分析
薪資預測
基于決策樹和隨機森林,可以選擇不同的城市、學歷、就業單位進行預測
職位推薦
收藏夾
職位檢索
管理員功能
用戶管理
職位管理
其他基礎功能
登錄、注冊