项目
我正在进行一个小项目,用户可以创建事件(例如Eat
、Sleep
、Watch a movie
等)并记录与这些事件匹配的日志条目。
我的数据模型如下所示(应用程序本身是用Python 3 / Django编写的,但我认为这在这里并不重要):
# 一个事件示例event = { 'id': 1, 'name': 'Eat',}# 一个条目示例entry = { 'event_id': event['id'], 'user_id': 12, # 记录条目的日期 'date': '2017-03-16T12:56:32.095465+00:00', # 用户为条目提供的标签 'tags': ['home', 'delivery'], # 用户对条目的评分,可以是正面或负面 'score': 2, 'comment': 'That was a tasty meal',}
用户可以为任意数量的事件记录任意数量的条目,他们在需要时可以创建新事件。数据存储在关系数据库中。
现在,我想通过在用户访问“添加条目”表单时建议相关事件来简化数据输入。目前,他们可以在下拉菜单中选择与条目对应的活动,但我希望在此基础上建议他们几个相关事件。
我想,通过给定用户历史(所有记录的条目),应该可以通过识别条目中的模式来预测可能的输入,例如:
Eat
通常每天中午和晚上7:00左右发生Sleep
通常在晚上10:00之后发生Watch a movie
通常在星期五晚上8:00之后发生
理想情况下,我希望有一个函数,给定用户ID和日期时间,并使用用户历史,返回一个更可能发生的事件列表:
def get_events(user_id, datetime, max=3): # 实现 # 返回最多max个事件的列表 return events
所以,如果我以上面的例子为例(使用更人性化的日期),我会得到以下结果:
>>> get_events(user_id, 'Friday at 9:00 PM')['Watch a movie', 'Sleep', 'Eat']>>> get_events(user_id, 'Friday at 9:00 PM', max=2)['Watch a movie', 'Sleep']>>> get_events(user_id, 'Monday at 9:00 PM')['Sleep', 'Eat', 'Watch a movie']>>> get_events(user_id, 'Monday at noon')['eat']
当然,在现实生活中,我会传递真实的日期时间,并且我希望得到一个事件ID,以便我可以从数据库中获取相应的数据。
我的问题
(抱歉,如果解释整个事情花了一些时间)
我的实际问题是,实现这个需要哪些实际的算法/工具/库?这是否可行?
我目前的猜测是我需要使用一些高级的机器学习技术,使用像scikit-learn这样的工具和分类器,利用用户历史来训练它,然后让整个系统发挥它的魔力。
我对机器学习完全不熟悉,我担心自己没有足够的数学/科学背景来自己开始。你能提供一些参考资料来帮助我理解如何解决这个问题,我需要深入了解的算法/术语,或者一些伪代码吗?
回答:
我认为k近邻算法(kNN)是一个好的起点。在这种特定情况下,思路是寻找与给定时间最接近的k个事件,并计算最常发生的事件。
示例
假设你有输入
Friday at 9:00 PM
。计算数据库中所有事件与此日期的距离,并按升序排列。例如,如果我们以分钟为单位计算数据库中所有元素的距离,一个示例排名可能如下所示。('Eat', 34)('Sleep', 54)('Eat', 76) ...('Watch a movie', 93)
接下来你选择前k = 3个,并计算它们出现的频率,
('Eat', 2)('Sleep', 1)
这样函数返回
['Eat', 'Sleep']
(按此顺序)。
选择一个合适的k值很重要。太小的值会让偶然的异常值(在特定时刻做一次某事)对结果产生很大影响。选择太大的k值会使不相关的事件被包含在计数中。一种缓解这种情况的方法是使用距离加权kNN(见下文)。
选择距离函数
正如评论中提到的,使用两个时间戳之间的简单距离可能会丢失一些信息,例如星期几。我们可以通过使距离函数d(e1, e2)
稍微复杂一些来解决这个问题。在这种情况下,我们可以选择它作为时间和星期几之间的权衡,例如
d(e1, e2) = a * |timeOfDay(e1) - timeOfDay(e2)| * (1/1440) + b * |dayOfWeek(e1) - dayOfWeek(e2)| * (1/7)
其中我们通过一天中的最大时间差异(以分钟计)和星期几的最大差异来归一化这两个差异。a
和b
是可以用来给这些差异之一增加权重的参数。例如,如果我们选择a = 3
和b = 1
,我们说在同一天发生比在同一时间发生重要三倍。
距离加权kNN
你可以通过不仅仅选择k个最接近的元素,而是根据它们与给定点的距离为所有事件分配一个权重(例如距离)来增加复杂性(希望也能提高性能)。设e
为输入示例,o
为数据库中的一个示例。然后我们计算o
相对于e
的权重为
1w_o = --------- d(e, o)^2
我们看到,点随着它们与e
的距离增加而更快地失去权重。在你的情况下,需要从最终排名中选择一些元素。这可以通过对相同事件的权重求和来计算事件类型的最终排名来完成。
实现
kNN的好处在于它非常容易实现。你大致需要以下组件。
- 距离函数
d(e1, e2)
的实现。 -
根据此函数和给定输入示例对数据库中所有元素进行排序的函数。
def rank(e, db, d): """ 使用距离函数d对数据库db中的示例相对于e进行排序。 """ return sorted([(o, d(e, o)) for o in db], key=lambda x: x[1])
- 从此排名中选择一些元素的函数。