为协同过滤产品推荐创建矩阵的方法

Approaches for creating a matrix for collaborative filtering product recommendations

我正在探索 python 中的推荐系统,到目前为止,我已经使用 KNN 模型通过 'users like you also purchased...' 方法来推荐品牌。我的数据 table 有一行代表每个客户,一列代表每个品牌,填充有 10 以表明客户是否购买了该品牌。

我现在希望将其推进到产品级建议,但很难看到这种方法将如何扩展。我尝试了相同的方法,但无法使用大到足以为每个产品 (10,000+) 生成一列的查询来查询我的数据库 (BigQuery)。

例如,我的来源是 Google 导出到 BigQuery 的 Analytics 每日数据,我正在根据以下示例创建我的输入数据:

SELECT
  customDimension.value AS UserID,
  MAX(IF(UPPER(hits_product.productSKU) LIKE "SKU1",1,0)) AS SKU1,
  MAX(IF(UPPER(hits_product.productSKU) LIKE "SKU2",1,0)) AS SKU2,
  MAX(IF(UPPER(hits_product.productSKU) LIKE "SKU3",1,0)) AS SKU3
  # plus 10,000 more...
  FROM
  `PROJECT.DATASET.ga_sessions_20*` AS t
CROSS JOIN
  UNNEST (hits) AS hits
CROSS JOIN
  UNNEST(t.customdimensions) AS customDimension
CROSS JOIN
  UNNEST(hits.product) AS hits_product
WHERE
  parse_DATE('%y%m%d',
    _table_suffix) BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 1 day)
  AND DATE_SUB(CURRENT_DATE(), INTERVAL 1 day)
  AND customDimension.index = 2
  AND customDimension.value NOT IN ("true","false","undefined")
  AND customDimension.value IS NOT NULL
  AND hits.eventInfo.eventCategory = 'Ecommerce'
  AND hits.eventInfo.eventAction = 'Purchase'
GROUP BY
  UserID

运行 每个 SKU 对应一行的查询生成错误:

The query is too large. The maximum query length is 256.000K characters, including comments and white space characters.

在这种情况下,如何创建产品级推荐?数据是否通常以不同的形式摄入 python 并在代码中转换为 maxrix?

此时我完全不知所措,欢迎提出任何建议。

我不确定如何在 BigQuery(或 SQL 的任何方言)中有效地创建你想要的 1-0(one-hot-esque)编码,但我绝对知道如何制作它在 Python.

聚合这些数据以用于 Python 的最有效方法可能是执行以下操作...

看起来您的 BigQuery table 遵循以下结构:

开始,您似乎可以使用如下方式将每个 SKU 聚合到一行中:

SELECT UserID, STRING_AGG(SKU) AS SKU_string FROM my_transactions_table GROUP BY UserID

哪个应该给你这个(以上面的例子 table 为例):

从那里开始,在 Python:

中使用这些数据真的很容易
>>> import pandas as pd
>>> df = pd.read_csv('~/Desktop/test.csv', sep='\t')
>>> df
   UserID SKU_string
0       1      a,b,c
1       2        b,b
2       3      c,b,a

我们可以使用 scikit-learn 的 CountVectorizer class 来计算每个用户每个产品的出现次数:

>>> from sklearn.feature_extraction.text import CountVectorizer
>>> vec = CountVectorizer(tokenizer=lambda x: x.split(','))
>>> X = vec.fit_transform(df['SKU_string'])
>>> X
<3x3 sparse matrix of type '<class 'numpy.int64'>'
    with 7 stored elements in Compressed Sparse Row format>
>>> pd.DataFrame(X.toarray(), columns=vec.get_feature_names())
   a  b  c
0  1  1  1
1  0  2  0
2  1  1  1

如果愿意,将该矩阵连接回 DataFrame 和您可能选择的其他用户元数据:

>>> df = df.join(pd.DataFrame(X.toarray(), columns=['product_{}'.format(x) for x in vec.get_feature_names()]))
>>> df
   UserID SKU_string  product_a  product_b  product_c
0       1      a,b,c          1          1          1
1       2        b,b          0          2          0
2       3      c,b,a          1          1          1

但是,如果您有很多不同的产品,我很可能不建议这样做。 10,000 个产品会创建 10,000 个额外的 非稀疏 列,如果您有很多客户,这会占用大量内存。

此外,如果您想将 X 对象(scipy.sparse.csr_matrix)严格转换为零编码,请尝试以下操作:

>>> import numpy as np
>>> import scipy.sparse
>>> def booleanize_csr_matrix(mat):
...     ''' Convert sparse matrix with positive integer elements to 1s '''
...     nnz_inds = mat.nonzero()
...     keep = np.where(mat.data > 0)[0]
...     n_keep = len(keep)
...     result = scipy.sparse.csr_matrix(
...             (np.ones(n_keep), (nnz_inds[0][keep], nnz_inds[1][keep])),
...             shape=mat.shape
...     )
...     return result
... 
>>> pd.DataFrame(booleanize_csr_matrix(X).toarray(), columns=vec.get_feature_names())
     a    b    c
0  1.0  1.0  1.0
1  0.0  1.0  0.0
2  1.0  1.0  1.0

从那里,您可以使用各种算法根据用户推荐项目...您可以查看 sklearn.metrics.pairwise.cosine_similarity 来测量每个用户的购买向量之间的角度。

通常,当我们的 sql 查询看起来更像服务器日志(又长又长)时,可能是时候重新考虑数据的策略和结构并尝试设计解决方法了。

在您的特定情况下,您正在尝试构建具有绝对元素的查询,这通常不是一个好的做法。因此,您需要做的是将您的 skus(所有这些)转储到 BigQuery table 中。完成后,您可以在 BigQuery 中使用 ARRAYS 来生成您的单热编码(以及其他)。这是一个简短的示例,使用 public GA 数据:

with listskus as (
  -- this is fake data. 
  -- replace it with your sku listing query (i.e. select sku as listsku from myskutable)
  select 
    listsku from 
    unnest(generate_array(0, 11000, 1)) 
  as listsku
),
data as (
  select 
    visitId as userid,
    array(
      (
        select 
          if(p.productSKU like concat('%',cast(l.listsku as string)), 1, 0) 
        from unnest(hits.product) p 
        left outer join listskus l on 1=1
      )
    ) as onehotvector
  from 
  `bigquery-public-data.google_analytics_sample.ga_sessions_20170801`, 
  unnest(hits) hits
)
select userid, onehotvector from data