用Python构建Amazon产品推荐系统!这项目价值能有2000吗?

news2024/11/27 0:30:07

引言

该项目的目标是部分重建Amazon电子类产品的推荐系统。

现在是十二月!你是什么类型的购物者?双十二都购买了吗?你是把当天想购买的所有产品都保存起来,还是宁愿打开网站,查看带有巨大折扣的现场优惠呢?

尽管网店在过去十年中取得了令人难以置信的成功,显示出巨大的潜力和增长势头,但实体店和网店的基本区别之一却是消费者的冲动购买。

如果你主动向客户展示了一系列产品,他们更有可能购买他们原本没有计划购买的产品。冲动购买的现象受到网店配置的极大限制。同样的情况在实体店却不会发生。最大的实体零售连锁店让顾客走一条精确的路径,以确保他们在离开商店之前参观商店的每一条货架间的过道。

像Amazon这样的网店可以通过推荐系统重现冲动购买现象。推荐系统识别客户刚刚购买或观看的最相似或互补的产品。其目的是将网店通常缺乏的随机购买现象最大化。

在Amazon 上购买使我对机制非常感兴趣,我想重新创建(实际上是部分重新)他们推荐系统的结果。

根据博客“Recostream”,亚马逊产品推荐系统有三种依赖关系,其中一种是产品对产品的推荐。当用户实际上没有历史搜索记录时,该算法将产品聚类在一起,并根据项目的元数据将其推荐给同一用户。

数据

        项目的第一步是收集数据。幸运的是,圣地亚哥加州大学的研究人员有一个可以让学生和组织外的个人使用数据进行研究和项目的存储库。可以通过以下链接http://jmcauley.ucsd.edu/data/amazon/links.html)访问数据以及与推荐系统相关的许多有趣的数据集[2][3]。产品元数据上次更新于2014年;很多产品今天可能都没有了。

电子类元数据包含了498196条记录,共有8列:

  • asin—与每个产品关联的唯一ID

  • imUrl—与每个产品关联的图像URL链接

  • description—产品说明

  • categories —每个产品所属类别的python列表

  • title—产品的名称

  • price—产品的价格

  • salesRank—特定类别中每个产品的排名

  • related —与每个产品相关的客户查看和购买的产品

  • brand—产品的品牌。

你将注意到该文件是“松散”的JSON格式,其中每一行都是一个JSON,它包含了前面提到的所有列以作为其中一个字段。我们将在代码部署部分了解如何处理此问题。

EDA

让我们从快速探索性数据分析开始。在清理了其中一列中至少包含NaN值的所有记录后,我为电子类产品创建了可视化。

带异常值的价格箱形图——图像由作者提供

第一个图表是显示每个产品的最高、最低、第25百分位、第75百分位和平均价格的箱形图。例如,我们知道一个产品的最高价值是1000美元,而最低价值大约是1美元。160美元标记上方的线是由点组成的,每个点都标识一个异常值。在整个数据集中,异常值表示只发生一次的记录。因此,我们只知道一种定价在1000美元左右的产品。

平均价格似乎在25美元左右。需要注意的是,matplotlib以选项showfliers=False自动排除异常值,为了使框图看起来更清晰,我们可以将参数设置为false。

价格箱形图——图像由作者提供

结果是一个不带异常值的更清晰的箱形图。该图表还表明,绝大多数电子产品的价格在1美元至160美元之间。

产品数量排名前十的品牌——作者图片

 

该图表显示了在Amazon上销售的电子类产品中排名前十的品牌。其中包括惠普、索尼、戴尔和三星。

十大零售商价格箱形图

最后,我们可以看到前十名卖家的价格分布形式。索尼和三星提供的产品种类繁多,从几美元到500美元和600美元不等,因此,它们的平均价格高于大多数顶级竞争对手。有趣的是,SIB和SIB-CORP提供了更多的产品,但平均价格更实惠。

该图表还告诉我们,索尼提供的产品大约占数据集中价格最高产品的60%。

余弦相似性

一种通过产品的特性将产品聚集在一起的方案就是余弦相似性。我们需要彻底理解这个概念,才能构建我们的推荐系统。

余弦相似性度量两个数字序列的“接近”程度。它如何适用于我们的案例?令人惊讶的是,句子可以转换成数字,或者更好地转换成向量。

余弦相似度可以取-1和1之间的值,其中1表示两个向量在形式上相同,而-1表示它们尽可能不同。

数学上,余弦相似性是两个多维向量的点积除以其大小的乘积[4]。我知道这里有很多难以理解的词,让我们用一个实际的例子来解释一下吧。

由Embed Fun提供

假设我们正在分析文档A和文档B。文档A有三个最常见的术语:“今天”、“好”和“阳光”,它们分别出现4次、2次和3次。文档B中这三个术语出现了3、2和2次。因此,我们可以这样写:

A=(2,2,3);B=(3,2,2)

两个向量的点积公式可以写成:

由Embed Fun提供

向量点积:2x3+2x2+3x2=16

另一方面,单向量值的计算如下:

应用以上公式可得:||A|| = 4.12 ; ||B|| = 4.12

因此它们的余弦相似性为:16 / 17 = 0.94 = 19.74°

这两个向量非常相似。

到目前为止,我们只计算了两个三维向量之间的值。实际上,一个词向量可以有无限个维度(取决于它包含的单词数量),但这个过程背后的逻辑在数学上是相同的。在下一节中,我们将了解如何在实践中应用所有概念。

代码编写

让我们进入代码编写阶段——在数据集上构建推荐系统。

每个数据科学笔记本的第一个单元都应该导入数据库,我们项目需要的代码是:

#Importing libraries for data management
import gzip
import json
import pandas as pd
from tqdm import tqdm_notebook as tqdm

#Importing libraries for feature engineering
import nltk
import re
from nltk.corpus import stopwords
from sklearn.feature_extraction.text import CountVectorizer 
from sklearn.metrics.pairwise import cosine_similarity
  • gzip:解压数据文件

  • json :解码

  • pandas:将JSON数据转换为更易于管理的数据帧格式

  • tqdm:创建进度条

  • nltk :处理文本字符串

  • re:提供正则表达式支持

  • sklearn :文本预处理

读取数据

        如前所述,数据以松散的JSON格式上传。解决此问题的方法首先是使用命令 json.dumps将文件转换为JSON可读格式行。然后,我们可以将此文件设置为换行符,并将其转换为由JSON行组成的python列表。最后,我们可以将每一行添加到数据空列表中,同时使用命令 json.loads 将其以JSON的格式读取。

使用命令pd.DataFrame,数据列表被读取为数据帧,我们现在可以使用它来构建推荐器。

#Creating an empty list
data = []

#Decoding the gzip file
def parse(path):
  g = gzip.open(path, 'r')
  for l in g:
    yield json.dumps(eval(l))

#Defining f as the file that will contain json data
f = open("output_strict.json", 'w')

#Defining linebreak as '\n' and writing one at the end of each line
for l in parse("meta_Electronics.json.gz"):
  f.write(l + '\n')

#Appending each json element to the empty 'data' list
with open('output_strict.json', 'r') as f:
    for l in tqdm(f):
        data.append(json.loads(l))

#Reading 'data' as a pandas dataframe
full = pd.DataFrame(data)

为了让你了解数据列表的每一行,我们运行了一个简单的命令print(data[0]),在索引0处控制台打印即可。


print(data[0])

output: 
{
'asin': '0132793040', 
'imUrl': 'http://ecx.images-amazon.com/images/I/31JIPhp%2BGIL.jpg', 
'description': 'The Kelby Training DVD Mastering Blend Modes in Adobe Photoshop CS5 with Corey Barker is a useful tool for...and confidence you need.', 
'categories': [['Electronics', 'Computers & Accessories', 'Cables & Accessories', 'Monitor Accessories']], 
'title': 'Kelby Training DVD: Mastering Blend Modes in Adobe Photoshop CS5 By Corey Barker'
}

正如你所看到的一样,输出的是一个JSON文件,它有{}来打开和关闭字符串,每个列名后面都跟着相应的字符串。你可以注意到,第一个产品缺少price、 salesRank、related和brand这些列将自动填充NaN值。

一旦我们用dataframe阅读整个列表,电子产品将显示以下八个特征:


| asin   | imUrl   | description   | categories   |
|--------|---------|---------------|--------------|
| price   | salesRank   | related   | brand   |
|---------|-------------|-----------|---------|

特征工程

       特征工程负责清理数据和创建专栏,我们将在其中计算余弦相似性分数。由于RAM内存的限制,我不希望专栏特别长,正如评论或产品描述一样。相反,我决定用categories、 title、 和brand 创建一个“数据包”。但在此之前,我们需要消除这三列中任一包含NaN值的行。

        所选栏目以我们推荐人所需文本的形式包含有价值的重要信息。描述列也可能是一个潜在的候选项,但字符串通常太长,并且没有在数据集中进行标准化。对于我们正在努力实现的目标来说,它并不代表足够可靠的信息。


#Dropping each row containing a NaN value within selected columns
df = full.dropna(subset=['categories', 'title', 'brand'])

#Resetting index count
df = df.reset_index()

在运行第一部分代码后,行数从498196惊人地减少到大约142000,这是一个很大的变化。只有在这一点上,我们才能创建所谓的数据包:


#Creating datasoup made of selected columns
df['ensemble'] = df['title'] + ' ' + 
df['categories'].astype(str) + ' ' + 
df['brand']

#Printing record at index 0
df['ensemble'].iloc[0]

output: 
"Barnes & Noble NOOK Power Kit in Carbon BNADPN31 
[['Electronics', 'eBook Readers & Accessories', 'Power Adapters']] 
Barnes & Noble"

因为标题并不总是包含其名称,所以需要添加品牌名称。

        现在我可以进入清理部分了。text_cleaning函数负责从集合列中删除每个 amp字符串。除此之外,字符串[^A-Za-z0–9]过滤掉所有的特殊字符。最后,函数的最后一行消除了字符串包含的每个停止语。


#Defining text cleaning function
def text_cleaning(text):
    forbidden_words = set(stopwords.words('english'))
    text = re.sub(r'amp','',text)
    text = re.sub(r'\s+', ' ', re.sub('[^A-Za-z0-9]', ' ', 
           text.strip().lower())).strip()
    text = [word for word in text.split() if word not in forbidden_words]
    return ' '.join(text)

使用lambda函数,我们可以将 text_cleaning应用于称为ensemble 中,我们可以通过调用iloc并指示随机记录的索引来随机选择随机产品的数据包。


#Applying text cleaning function to each row
df['ensemble'] = df['ensemble'].apply(lambda text: text_cleaning(text))

#Printing line at Index 10000
df['ensemble'].iloc[10000]

output:
'vcool vga cooler electronics computers accessories 
computer components fans cooling case fans antec'

第10001行的记录(索引从0开始)是Antec的vcool VGA cooler。这是一个标题不包括品牌的例子。

余弦运算和推荐函数

        余弦相似性的运算从建立一个包含集合列中出现的所有单词的矩阵开始。我们将使用的方法称为“计数矢量化”,或者更常见的“单词包”。

        由于RAM的限制,余弦相似性分数仅在预处理阶段后可用的142000条记录中的前35000条记录上进行运算。这很可能会影响推荐者的最终表现。


#Selecting first 35000 rows
df = df.head(35000)

#creating count_vect object
count_vect = CountVectorizer()

#Create Matrix
count_matrix = count_vect.fit_transform(df['ensemble'])

# Compute the cosine similarity matrix
cosine_sim = cosine_similarity(count_matrix, count_matrix)

        顾名思义,命令cosine_similarity计算count_matrix中每一行的余弦相似度。count_matrix上的每一行都是一个向量,其中包含集合列中出现的每个单词的字数。

#Creating a Pandas Series from df's index
indices = pd.Series(df.index, index=df['title']).drop_duplicates()

在运行实际的推荐系统之前,我们需要确保创建一个索引,并且该索引没有重复项。

     只有通过这,我们才能定义content_recommender功能。它有4个参数:title 、cosine_sim、 df和 indices。在调用函数时,标题将是唯一要输入的元素。

content_recommender的工作方式如下:

  • 查找与用户提供的标题相关联的产品索引

  • 在余弦相似度矩阵中搜索产品的索引,并收集所有产品的所有得分

  • 将所有得分从最相似的产品(接近1)排序到最不相似的(接近0)

  • 选择前30种最相似的产品

  • 添加一个索引并以结果的形式返回pandas序列


# Function that takes in product title as input and gives recommendations
def content_recommender(title, cosine_sim=cosine_sim, df=df,
indices=indices):
        
    # Obtain the index of the product that matches the title
    idx = indices[title]
        
    # Get the pairwsie similarity scores of all products with that product
    # And convert it into a list of tuples as described above
    sim_scores = list(enumerate(cosine_sim[idx]))
        
    # Sort the products based on the cosine similarity scores
    sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
        
    # Get the scores of the 30 most similar products. Ignore the first product.
    sim_scores = sim_scores[1:30]
        
    # Get the product indices
    product_indices = [i[0] for i in sim_scores]
        
    # Return the top 30 most similar products
    return df['title'].iloc[product_indices]

现在让我们在“Vcool VGA冷却器”上测试它。我们想要三十种类似并且客户会有兴趣购买的产品。通过运行命令content_recommender(product_title) ,该函数将返回一个包含三十条建议的列表。

#Define the product we want to recommend other items from
product_title = 'Vcool VGA Cooler'

#Launching the content_recommender function
recommendations = content_recommender(product_title)

#Associating titles to recommendations
asin_recommendations = df[df['title'].isin(recommendations)]

#Merging datasets
recommendations = pd.merge(recommendations, 
                           asin_recommendations, 
                           on='title', 
                           how='left')

#Showing top 5 recommended products
recommendations['title'].head()

在5种最相似的产品中,我们发现了其他Antec产品,如Tricool电脑机箱风扇、扩展槽冷却风扇等等。

1    Antec Big Boy 200 - 200mm Tricool Computer Case Fan                                                            
2    Antec Cyclone Blower, Expansion Slot Cooling Fan                                                               
3    StarTech.com 90x25mm High Air Flow Dual Ball Bearing Computer Case Fan with TX3 Cooling Fan FAN9X25TX3H (Black)
4    Antec 120MM BLUE LED FAN Case Fan (Clear)                                                                      
5    Antec PRO 80MM 80mm Case Fan Pro with 3-Pin & 4-Pin Connector (Discontinued by Manufacturer)

原始数据集中的相关列中包含了消费者购买、一起购买和观看VGA cooler后购买的产品列表。

#Selecting the 'related' column of the product we computed recommendations for
related = pd.DataFrame.from_dict(df['related'].iloc[10000], orient='index').transpose()

#Printing first 10 records of the dataset
related.head(10)

 通过在该列中打印python字典的头部,控制台将返回以下数据集。


|    | also_bought   | bought_together   | buy_after_viewing   |
|---:|:--------------|:------------------|:--------------------|
|  0 | B000051299    | B000233ZMU        | B000051299          |
|  1 | B000233ZMU    | B000051299        | B00552Q7SC          |
|  2 | B000I5KSNQ    |                   | B000233ZMU          |
|  3 | B00552Q7SC    |                   | B004X90SE2          |
|  4 | B000HVHCKS    |                   |                     |
|  5 | B0026ZPFCK    |                   |                     |
|  6 | B009SJR3GS    |                   |                     |
|  7 | B004X90SE2    |                   |                     |
|  8 | B001NPEBEC    |                   |                     |
|  9 | B002DUKPN2    |                   |                     |
| 10 | B00066FH1U    |                   |                     |

让我们测试一下我们的推荐怎么样。让我们看看建议中是否包含了also_bought 列表中的一些asin ID。

#Checking if recommended products are in the 'also_bought' column for
#final evaluation of the recommender

related['also_bought'].isin(recommendations['asin'])

我们正确地推荐了44种产品中的5种。

[True False  True False False False False False False False  True False False False False False False  True False False False False False False False False  True False False False False False False False False False False False False False False False False False]

我不同意这是最佳的结果,但考虑到我们在整个数据集中仅使用了498196行中的35000行,这是还是可以接受的。它当然有很大的改进空间。如果目标列的NaN值不那么频繁,甚至不存在,那么建议可能会更准确,甚至更接近Amazon 的实际值。其次是访问更大的RAM内存,甚至是分布式计算,这可以让从业者计算更大的矩阵。

结论

我希望你喜欢这个项目并对将来的任何使用是有用的。

        如本文所述,通过将数据集的所有行包含在余弦相似度矩阵中,可以进一步改进最终结果。除此之外,我们还可以通过将元数据数据集与仓库中的其他数据集合并来添加每个产品的审查平均。我们可以在余弦相似度的计算中包含价格。另一种可能是完全基于每个产品的描述性图像来构建推荐系统。点击下方小卡片可以获取源代码,小编推荐一套零基础入门的视频,非常适合零基础看!

     进一步改进的主要方案已列出。从未来实施到实际生产的角度来看,其中大多数值得探索。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/86042.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从单服务器模式到负载均衡设计

从单服务器模式到负载均衡设计 作者:Grey 原文地址: 博客园:从单服务器模式到负载均衡设计 CSDN:从单服务器模式到负载均衡设计 单服务器模型是最简单的一种架构,参考如下图 用户访问一个 URL,URL 会…

java计算机毕业设计基于安卓Android的校园外卖点餐APP

项目介绍 餐饮行业是一个传统的行业。根据当前发展现状,网络信息时代的全面普及,餐饮行业也在发生着变化,单就点餐这一方面,利用手机点单正在逐步进入人们的生活。传统的点餐方式,不仅会耗费大量的人力、时间,有时候还会出错。Android系统伴随智能手机为我们提供了新的方向。手…

基于转换器 (MMC) 技术和电压源转换器 (VSC) 的高压直流 (HVDC) 模型(MatlabSimulink实现)

目录 1 概述 2 主要模块说明 2.1 简化电网 2.2 转换器 1 2.3 直流电路 2.4 控制器 2.5 示波器和测量 3 讲解 3.1 参数设置 3.2 SPS 比较 3.3 结果比较 3.4 参考文献 4 Matlab代码实现 1 概述 1000 MW HVDC-MMC 互连的 SPS 模型。本文基于模块化多电平转换器 (MMC)…

计算机网络-数据链路层:以太网协议、ARP协议、MAC、MTU

目录 一、以太网协议 1. 认识以太网 2. 协议格式 二、 MAC地址 1. 认识MAC地址 2. 对比MAC地址与IP地址 三、ARP协议 1. 认识ARP协议 2. ARP协议的作用 3. ARP协议的工作流程 4. ARP欺骗攻击 四、MTU 1. 认识MTU 2. MTU对IP协议的影响(了解&#xff…

在线教育系统源码讲解与代码分析

目前,许多行业已经开始向直播领域靠拢,例如直播带货、教育直播、娱乐直播等领域,想要在此分一杯羹,以在线教育系统来说,在2020年以后便进入了“白热化”,更多的直播、教育展现在大众视野中。在粉丝经济的时…

Linux的进程空间管理

用户态和内核态的划分 进程的虚拟地址空间,其实就是站在项目组的角度来看内存,所以我们就从task_struct出发来看。这里面有一个struct mm_struct结构来管理内存。 struct mm_struct *mm; 在struct mm_struct里面,有这样一个成员变量&#…

如何利用ArcGIS实现电子地图可视化表达?分析空间数据?提升SCI论文的层次?探究环境与生态因子对水体、土壤、大气污染物等影响?

查看原文>>>如何利用ArcGIS探究环境与生态因子对水体、土壤、大气污染物等影响 如何利用ArcGIS实现电子地图可视化表达?如何利用ArcGIS分析空间数据?如何利用ArcGIS提升SCI论文的层次?制图是地理数据展现的直观形式,也是…

ARM存储模型(数据存储、指令存储)

目录 1、ARM数据存储 (1) ARM数据类型 (2) ARM数据存储的方式 2、ARM的指令存储 (1) 指令集的分类 (2) 为什么ARM指令集的PC值与低2位无关? 1、ARM数据存储 (1) ARM数据类型 ARM采用32位架构,即ARM一次可以处理32bit的数据,基本的数据…

Linux--进程间通信(2)--共享内存--1126

1.共享内存的原理 进程A先申请一段内存空间,其页表映射到物理空间,进程B通过A的页表映射,将B的页表也同样映射到同一块物理空间。 释放共享内存 各自修改各自的页表指向并释放共享内存 2.共享内存的建立过程 头文件 comm.hpp #pragma o…

Windows系统dos命令之cmd

目录1. 使用cd命令快速切换到指定的盘符中1.1 参数说明1.2 cd 切换盘符2. 使用cd命令切换到指定的目录中2.1 切换指定目录3. 使用cd命令退回到上一层目录3.1 使用命令 ”cd ..“4. 使用cd命令直接退回到当前根目录下4.1 使用命令cd \1. 使用cd命令快速切换到指定的盘符中 cd 是…

JUC并发编程第十一篇,Java对象的内存布局

JUC并发编程第十一篇,Java对象的内存布局一、对象在堆内存中的存储布局1、对象头对象标记Mark Word类元信息(类型指针)2、实例数据3、对齐填充二、对象标记(MarkWord)布局与验证代码验证(JOL)一…

重点| 系统集成项目管理工程师考前50个知识点(4)

本文章总结了系统集成项目管理工程师考试背记50个知识点!!! 帮助大家更好的复习,希望能对大家有所帮助 比较长,放了部分,需要可私信!! 26、编制范围管理计划的输出: 范…

Flink Window Function

窗口函数定义了要对窗口中收集的数据做的计算操作,根据处理的方式可以分为两类:增量聚合函数和全窗口函数。 文章目录1.增量聚合函数1.1 ReduceFunction1.2 AggregateFunction2.全窗口函数2.1 WindowFunction2.2 ProcessWindowFunction3.增量聚合和全窗口…

【MySQL】用户管理

文章目录用户管理用户信息创建用户删除用户修改用户密码用户权限用户赋权回收权限用户管理 如果我们只能使用root用户,这样存在安全隐患,因为root可以访问所有的数据库和表,这时,就需要使用MySQL的用户管理,从而限制某个特定的用户只能访问特定的数据库和表,并且对其权限作出一…

【51-订单模块-资源整合-整合SpringSession-订单中心核心逻辑-认证拦截-订单提交-订单生成逻辑-接口幂等性处理-接口幂等性解决方案】

一.知识回顾 【0.三高商城系统的专题专栏都帮你整理好了,请点击这里!】 【1-系统架构演进过程】 【2-微服务系统架构需求】 【3-高性能、高并发、高可用的三高商城系统项目介绍】 【4-Linux云服务器上安装Docker】 【5-Docker安装部署MySQL和Redis服务】…

若依上面添加layui组件

1.因为若依本身有layui&#xff0c;所以不需要在引layui的css与js 2.下面是需要添加的 <div className"layui-btn-container" style"margin-top: 30px;"><button data-method"setTop" οnclick"sjhdekjshd()">多窗口模式…

基于vite搭建的vue3项目中如何引用环境变量

目录 回顾一下vue-cli2/cli3中环境变量使用方法 环境变量定义&#xff1a; 环境变量使用&#xff1a; vite中环境变量使用方法 环境变量定义&#xff1a; 环境变量使用&#xff1a; 回顾一下vue-cli2/cli3中环境变量使用方法 在vue-cli2/cli3中使用环境变量时 环境变量定…

Python Flask 路由配置

有关更多的Python 开发内容,可访问:《 Python Flask开发指南》 Flask中通过使用route装饰器实现路由访问功能,其路由匹配URL规则基于Werkzeug的路由模块。该模块基于Apache及更早的HTTP服务器主张,希望保证优雅且唯一的URL。其使用格式如下: from flask import Flask app …

突然 Java 倒下了......

TIOBE 公布了 2022 年 12 月的编程语言排行榜。 Java 首次跌出前 3 名。除此之外&#xff0c;Kotlin 和 Julia 也越来越接近 Top 20。 TIOBE 将于下个月揭晓其 2022 年度编程语言&#xff0c;目前共有 3 个候选者&#xff1a;Python、C 和 C。TIOBE CEO Paul Jansen指出&#…

如何实现工具无关化?——关于自动化测试脚本的设计

问题的提出 最近几年来&#xff0c;我的自动化测试工具之旅大致是这样的&#xff0c;最早用的是QTP&#xff0c;然后是RFT&#xff08;IBM的功能测试自动化产品&#xff09;&#xff0c;之后也经历了Selenium&#xff0c;Watir等。再后来还是一些商业工具&#xff0c;主要是偏…