From f6800ba8bf82ee381dab96cdf390ee509a9001b8 Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Mon, 2 Oct 2023 16:26:26 +0300 Subject: [PATCH 01/18] fixed cyclic imports --- rectools/metrics/diversity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rectools/metrics/diversity.py b/rectools/metrics/diversity.py index 3db20751..59ed8015 100644 --- a/rectools/metrics/diversity.py +++ b/rectools/metrics/diversity.py @@ -22,10 +22,10 @@ import pandas as pd from rectools import Columns -from rectools.metrics import PairwiseDistanceCalculator from rectools.utils import select_by_type from .base import MetricAtK +from .distances import PairwiseDistanceCalculator @attr.s From a374c289795712d8ba6f24463ff56a05e09e4702 Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Mon, 2 Oct 2023 16:26:53 +0300 Subject: [PATCH 02/18] updated implicit to 0.7.1 --- poetry.lock | 219 ++++++++++++++++++++++++++++++------------------- pyproject.toml | 2 +- 2 files changed, 134 insertions(+), 87 deletions(-) diff --git a/poetry.lock b/poetry.lock index c7050a06..70b6d082 100644 --- a/poetry.lock +++ b/poetry.lock @@ -128,14 +128,14 @@ frozenlist = ">=1.1.0" [[package]] name = "astroid" -version = "2.15.7" +version = "2.15.8" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false python-versions = ">=3.7.2" files = [ - {file = "astroid-2.15.7-py3-none-any.whl", hash = "sha256:958f280532e36ca84a13023f15cb1556fb6792d193acb87e1f3ca536b6fa6bd2"}, - {file = "astroid-2.15.7.tar.gz", hash = "sha256:c522f2832a900e27a7d284b9b6ef670d2495f760ede3c8c0b004a5641d3c5987"}, + {file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"}, + {file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"}, ] [package.dependencies] @@ -301,87 +301,102 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.2.0" +version = "3.3.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, - {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, + {file = "charset-normalizer-3.3.0.tar.gz", hash = "sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-win32.whl", hash = "sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-win32.whl", hash = "sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-win32.whl", hash = "sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-win32.whl", hash = "sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-win32.whl", hash = "sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-win32.whl", hash = "sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884"}, + {file = "charset_normalizer-3.3.0-py3-none-any.whl", hash = "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2"}, ] [[package]] @@ -728,13 +743,45 @@ files = [ [[package]] name = "implicit" -version = "0.4.4" -description = "Collaborative Filtering for Implicit Datasets" +version = "0.7.1" +description = "Collaborative Filtering for Implicit Feedback Datasets" category = "main" optional = false python-versions = "*" files = [ - {file = "implicit-0.4.4.tar.gz", hash = "sha256:4ec4966a6e34c676695528bfee3465c48cf7e0ba49594ad96ffb7608c795c2f6"}, + {file = "implicit-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:819c841ab079a4448d125abec3dc7125f38ca6ca35c61b1d84e0a2ce82004ef9"}, + {file = "implicit-0.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c3a7f6571c4be720a56282724731c9e3de8171b1f9f478c7dd58f2b5697c95e0"}, + {file = "implicit-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b8dedefe30f3c66ad8190002aa3d66237a16d82e6600cfd3019353ae7e7288"}, + {file = "implicit-0.7.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:65280aeed25cc3f0eb85fb5367de1555a4a57f99ef245a3ab06455ba1eb9d07f"}, + {file = "implicit-0.7.1-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:d7de985e65f29df0b0e2dbfaceed3a8185a97f82e48998faa6cf70cca2845012"}, + {file = "implicit-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:3b7aa37e99eeba12bdf0ab3257425a5faf792591e1ff87e7d1cd43443c5f346b"}, + {file = "implicit-0.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ba7b6e128eb37a6eff4afdee442a945762bd40d147e8496e63222aca230e574"}, + {file = "implicit-0.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f7a778f9e971e82041e9e2edc801b16a5a9d2939040f702fbbc10aaf9be5f4c"}, + {file = "implicit-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d6241366d06b36a4e7f0de420b976728a939d7b6ba9aa3a9fc0147f4a4fba5c3"}, + {file = "implicit-0.7.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:90a02ec35d1fb807249c53a1fd8a2576085e14b198860d6965dc8accc897c845"}, + {file = "implicit-0.7.1-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:449c2eb872435bd026d9c5fa78c613242b1f73cc119f83d52d2a8948f87bc176"}, + {file = "implicit-0.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:59099d16c4e9e8c4e1f1ee1a4c3616f8276084265de824c8b9963e8347fff8ff"}, + {file = "implicit-0.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:db741e39650a891a06e6d2c3d0d7985c851650d520b5e37e9bec83bc736d43c1"}, + {file = "implicit-0.7.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c0ac423cbb20b863ce78481c2e120b292dec4422f081d2c3ca0b7b403d1a1167"}, + {file = "implicit-0.7.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:2fd7eaae32ffcd22844b172be2bbf5732b67c82937a64a041cac0b1dd71b1a9b"}, + {file = "implicit-0.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b4149835d6e78d5895576325e2688b4c538ec4995694f02d4e944bb18fc19d5b"}, + {file = "implicit-0.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b464c25f6365a274f9cba5b5b331291ec79a4e44cae1d86c770aee42afce1fdd"}, + {file = "implicit-0.7.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:4521e4e1d5939d04f9067ef350e3ed0d8bb4e68baf60a7882241c1c3f1a76454"}, + {file = "implicit-0.7.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:7ce66695defd8083eced0d041aad3dd6f4c7a22a14e3286aa432c187c3486db9"}, + {file = "implicit-0.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:303d210bf661308acfd36558854cbab4c45d23ed185fa545f5f4f746ab146328"}, + {file = "implicit-0.7.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9564c3309c8a3ba9a46e5c3c2e529c2e0a9caa30040069f47c1c6675f9efbcc1"}, + {file = "implicit-0.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4b847155b05f1ee23a3890622c8f8adc6e991524eb3ed92921d284c7f8eb6916"}, + {file = "implicit-0.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a0c113b74026b3dc15a2bc9c1a337c3d942b5d048ecfc45d3c5394613bb9ba1"}, + {file = "implicit-0.7.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:14851ae76fc959a4b26e49af2df8fbb221cbe76dd5b28d1298d1bac3d974a1e0"}, + {file = "implicit-0.7.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:56c288bfc646bae417a58740cff37e03ae344880653cfb75efbb61e4850e7f8e"}, + {file = "implicit-0.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:03e942f583caa52085ebeb5541ee7b29dd07210541e08e4d991ed78ab40b6045"}, + {file = "implicit-0.7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6493b354ea1ba032b71b5f30afc78563247fe2541a54973cedaed247d79a38f3"}, + {file = "implicit-0.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b0d1502022f11226f7a374bde621662586c422bc479ca5e8c647592b5aa981b"}, + {file = "implicit-0.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb098aba67ec6b6c36d1065076270bd060eadf6f422a9e99754fa843d29fd338"}, + {file = "implicit-0.7.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:df3d46072c21a777bf1b2d086b46731682e15db27e330519a80e2da367d5cefe"}, + {file = "implicit-0.7.1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:ce20282625feeceefae5a85f5290ddc957ecca1424646c10502fac978dde5aeb"}, + {file = "implicit-0.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:fd84392092ed985bd1c785c31daaec60d291d4012586927e4d486120c74d602b"}, + {file = "implicit-0.7.1.tar.gz", hash = "sha256:889c6a8f1e4c64eb5705890aa830625a7543d88757eb23ee5f6f4ba0caa8fcf3"}, ] [package.dependencies] @@ -1231,14 +1278,14 @@ wheel = "*" [[package]] name = "packaging" -version = "23.1" +version = "23.2" description = "Core utilities for Python packages" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] @@ -2325,4 +2372,4 @@ nn = ["pytorch-lightning", "torch"] [metadata] lock-version = "2.0" python-versions = ">=3.7.2, <3.10.0" -content-hash = "72682105c5a2ca0bd3df3af55c2fcdcaee30b91efe51f84a2423dd7b3d095d8c" +content-hash = "6e044af6b03abda7bbae6ee66bef5a06eee97f3798b96c728ed8931b53915589" diff --git a/pyproject.toml b/pyproject.toml index 94ea5a2d..77bc5bce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ numpy = ">=1.19.5, <2.0.0" pandas = ">=0.25.3, <2.0.0" scipy = "^1.5.4" tqdm = "^4.27.0" -implicit = "0.4.4" +implicit = "0.7.1" attrs = ">=19.1.0,<22.0.0" typeguard = "^2.0.1" lightfm = "1.17" From 7f7455479ea24d6d30471baa1cda5026c8cc58d3 Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Mon, 2 Oct 2023 17:26:28 +0300 Subject: [PATCH 03/18] updated implicit wrappers --- rectools/models/implicit_als.py | 20 +++++++++++--------- rectools/models/implicit_knn.py | 16 ++++++++-------- tests/models/test_implicit_als.py | 2 +- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/rectools/models/implicit_als.py b/rectools/models/implicit_als.py index 14396d3d..9a2d1323 100644 --- a/rectools/models/implicit_als.py +++ b/rectools/models/implicit_als.py @@ -17,7 +17,8 @@ from copy import deepcopy import numpy as np -from implicit.als import AlternatingLeastSquares +from implicit.cpu.als import AlternatingLeastSquares as CpuAlternatingLeastSquares +from implicit.gpu.als import AlternatingLeastSquares as GpuAlternatingLeastSquares from implicit.utils import check_random_state from scipy import sparse from tqdm.auto import tqdm @@ -29,6 +30,7 @@ MAX_GPU_FACTORS = 1024 AVAILABLE_RECOMMEND_METHODS = ("loop",) +AlternatingLeastSquares = tp.Union[CpuAlternatingLeastSquares, GpuAlternatingLeastSquares] class ImplicitALSWrapperModel(VectorModel): @@ -56,7 +58,7 @@ class ImplicitALSWrapperModel(VectorModel): def __init__(self, model: AlternatingLeastSquares, verbose: int = 0, fit_features_together: bool = False): super().__init__(verbose=verbose) - if model.use_gpu and model.factors > MAX_GPU_FACTORS: # pragma: no cover + if isinstance(model, GpuAlternatingLeastSquares) and model.factors > MAX_GPU_FACTORS: # pragma: no cover raise ValueError(f"When using GPU max number of factors is {MAX_GPU_FACTORS}") self.model: AlternatingLeastSquares self._model = model # for refit; TODO: try to do it better @@ -139,7 +141,7 @@ def fit_als_with_features_separately( Combined latent and explicit user factors. """ iu_csr = ui_csr.T.tocsr(copy=False) - model.fit(iu_csr, show_progress=verbose > 0) + model.fit(ui_csr, show_progress=verbose > 0) user_factors_chunks = [model.user_factors] item_factors_chunks = [model.item_factors] @@ -162,7 +164,7 @@ def fit_als_with_features_separately( def _fit_paired_factors(model: AlternatingLeastSquares, xy_csr: sparse.csr_matrix, y_factors: np.ndarray) -> np.ndarray: - if model.use_gpu: # pragma: no cover + if isinstance(model, GpuAlternatingLeastSquares): # pragma: no cover paired_factors = _fit_paired_factors_on_gpu(model, xy_csr, y_factors) else: paired_factors = _fit_paired_factors_on_cpu(model, xy_csr, y_factors) @@ -191,13 +193,13 @@ def _fit_paired_factors_on_gpu( y_factors: np.ndarray, ) -> np.ndarray: # pragma: no cover try: - from implicit.cuda import ( # pylint: disable=import-outside-toplevel + from implicit.gpu import ( # pylint: disable=import-outside-toplevel CuCSRMatrix, CuDenseMatrix, CuLeastSquaresSolver, ) except ImportError: - raise RuntimeError("implicit.cuda is not available") + raise RuntimeError("implicit.gpu is not available") n_factors = y_factors.shape[1] if n_factors > MAX_GPU_FACTORS: @@ -264,7 +266,7 @@ def fit_als_with_features_together( # Fix number of factors n_factors_all = model.factors + n_user_explicit_factors + n_item_explicit_factors - if model.use_gpu and n_factors_all % 32: # pragma: no cover + if isinstance(model, GpuAlternatingLeastSquares) and n_factors_all % 32: # pragma: no cover padding = 32 - n_factors_all % 32 warnings.warn( "GPU training requires number of factors to be a multiple of 32." @@ -303,7 +305,7 @@ def fit_als_with_features_together( ).astype(model.dtype) ui_csr = ui_csr.astype(np.float32) - if model.use_gpu: # pragma: no cover + if isinstance(model, GpuAlternatingLeastSquares): # pragma: no cover _fit_combined_factors_on_gpu_inplace( model, ui_csr, @@ -371,7 +373,7 @@ def _fit_combined_factors_on_gpu_inplace( verbose: int, ) -> None: # pragma: no cover try: - from implicit.cuda import ( # pylint: disable=import-outside-toplevel + from implicit.gpu import ( # pylint: disable=import-outside-toplevel CuCSRMatrix, CuDenseMatrix, CuLeastSquaresSolver, diff --git a/rectools/models/implicit_knn.py b/rectools/models/implicit_knn.py index 3a9efb9a..6a3e2b0b 100644 --- a/rectools/models/implicit_knn.py +++ b/rectools/models/implicit_knn.py @@ -49,8 +49,8 @@ def __init__(self, model: ItemItemRecommender, verbose: int = 0): def _fit(self, dataset: Dataset) -> None: # type: ignore self.model = deepcopy(self._model) - iu_csr = dataset.get_user_item_matrix(include_weights=True).T.tocsr(copy=False) - self.model.fit(iu_csr, show_progress=self.verbose > 0) + ui_csr = dataset.get_user_item_matrix(include_weights=True) + self.model.fit(ui_csr, show_progress=self.verbose > 0) def _recommend_u2i( self, @@ -97,16 +97,16 @@ def _recommend_for_user( if sorted_item_ids is not None: sorted_filtered_item_ids = sorted_item_ids[~fast_isin_for_sorted_test_elements(sorted_item_ids, viewed_ids)] n_items = user_items.shape[1] - reco_scores = self.model.recommend(user_id, user_items, N=n_items, filter_already_liked_items=False) - reco = np.array([r[0] for r in reco_scores]) - scores = np.array([r[1] for r in reco_scores]) + reco, scores = self.model.recommend( + user_id, user_items[user_id], N=n_items, filter_already_liked_items=False + ) valid_items_mask = fast_isin_for_sorted_test_elements(reco, sorted_filtered_item_ids) else: n_items = k + viewed_ids.size - reco_scores = self.model.recommend(user_id, user_items, N=n_items, filter_already_liked_items=False) - reco = np.array([r[0] for r in reco_scores]) - scores = np.array([r[1] for r in reco_scores]) + reco, scores = self.model.recommend( + user_id, user_items[user_id], N=n_items, filter_already_liked_items=False + ) valid_items_mask = fast_isin_for_sorted_test_elements(reco, viewed_ids, invert=True) reco = reco[valid_items_mask][:k] diff --git a/tests/models/test_implicit_als.py b/tests/models/test_implicit_als.py index 480eb03a..0c157e8e 100644 --- a/tests/models/test_implicit_als.py +++ b/tests/models/test_implicit_als.py @@ -18,7 +18,7 @@ import pandas as pd import pytest from implicit.als import AlternatingLeastSquares -from implicit.cuda import HAS_CUDA +from implicit.gpu import HAS_CUDA from rectools import Columns from rectools.dataset import Dataset, DenseFeatures, IdMap, Interactions, SparseFeatures From e647553180f57c995e9e59c87a8f5b3da21ffb74 Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Mon, 2 Oct 2023 18:08:12 +0300 Subject: [PATCH 04/18] updated poetry version in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e6370fe..7eec5f64 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ To install all requirements run ``` make install ``` -You must have `python3` and `poetry` installed. +You must have `python3` and `poetry>=1.4.0` installed. For autoformatting run ``` From 161d27eced0358ed3a239e0434964785853b6ca2 Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Tue, 3 Oct 2023 23:24:20 +0300 Subject: [PATCH 05/18] rewrote separate features training --- rectools/models/implicit_als.py | 276 ++------------------------------ 1 file changed, 16 insertions(+), 260 deletions(-) diff --git a/rectools/models/implicit_als.py b/rectools/models/implicit_als.py index 9a2d1323..c8a8c4c6 100644 --- a/rectools/models/implicit_als.py +++ b/rectools/models/implicit_als.py @@ -58,6 +58,9 @@ class ImplicitALSWrapperModel(VectorModel): def __init__(self, model: AlternatingLeastSquares, verbose: int = 0, fit_features_together: bool = False): super().__init__(verbose=verbose) + if fit_features_together: + raise NotImplementedError("We temporarily do not support fitting features together") + if isinstance(model, GpuAlternatingLeastSquares) and model.factors > MAX_GPU_FACTORS: # pragma: no cover raise ValueError(f"When using GPU max number of factors is {MAX_GPU_FACTORS}") self.model: AlternatingLeastSquares @@ -70,13 +73,7 @@ def _fit(self, dataset: Dataset) -> None: # type: ignore ui_csr = dataset.get_user_item_matrix(include_weights=True) if self.fit_features_together: - user_factors, item_factors = fit_als_with_features_together( - self.model, - ui_csr, - dataset.user_features, - dataset.item_features, - self.verbose, - ) + raise NotImplementedError("We temporarily do not support fitting features together") else: user_factors, item_factors = fit_als_with_features_separately( self.model, @@ -164,259 +161,18 @@ def fit_als_with_features_separately( def _fit_paired_factors(model: AlternatingLeastSquares, xy_csr: sparse.csr_matrix, y_factors: np.ndarray) -> np.ndarray: - if isinstance(model, GpuAlternatingLeastSquares): # pragma: no cover - paired_factors = _fit_paired_factors_on_gpu(model, xy_csr, y_factors) - else: - paired_factors = _fit_paired_factors_on_cpu(model, xy_csr, y_factors) - return paired_factors - - -def _fit_paired_factors_on_cpu( - model: AlternatingLeastSquares, - xy_csr: sparse.csr_matrix, - y_factors: np.ndarray, -) -> np.ndarray: - x_factors = np.zeros(shape=(xy_csr.shape[0], y_factors.shape[1]), dtype=y_factors.dtype) - model.solver( - xy_csr, - x_factors, - y_factors, - model.regularization, - model.num_threads, + feaures_model_params = dict( + factors=y_factors.shape[1], + regularization=model.regularization, + alpha=model.alpha, + dtype=model.dtype, + iterations=1, + random_state=model.random_state ) - return x_factors - - -def _fit_paired_factors_on_gpu( - model: AlternatingLeastSquares, - xy_csr: sparse.csr_matrix, - y_factors: np.ndarray, -) -> np.ndarray: # pragma: no cover - try: - from implicit.gpu import ( # pylint: disable=import-outside-toplevel - CuCSRMatrix, - CuDenseMatrix, - CuLeastSquaresSolver, - ) - except ImportError: - raise RuntimeError("implicit.gpu is not available") - - n_factors = y_factors.shape[1] - if n_factors > MAX_GPU_FACTORS: - raise ValueError(f"When using GPU max number of factors is {MAX_GPU_FACTORS}, here is {n_factors} factors") - - x_factors = np.zeros(shape=(xy_csr.shape[0], n_factors), dtype=y_factors.dtype) - x_cuda = CuDenseMatrix(x_factors) - y_cuda = CuDenseMatrix(y_factors) - xy_csr_cuda = CuCSRMatrix(xy_csr) - - solver = CuLeastSquaresSolver(n_factors) - solver.least_squares(xy_csr_cuda, x_cuda, y_cuda, model.regularization, model.cg_steps) - - x_cuda.to_host(x_factors) - return x_factors - - -def fit_als_with_features_together( - model: AlternatingLeastSquares, - ui_csr: sparse.csr_matrix, - user_features: tp.Optional[Features], - item_features: tp.Optional[Features], - verbose: int = 0, -) -> tp.Tuple[np.ndarray, np.ndarray]: - """ - Fit ALS model with explicit features, explicit features fit together with latent. - - Parameters - ---------- - model: AlternatingLeastSquares - Base model to fit. - ui_csr : sparse.csr_matrix - Matrix of interactions. - user_features : (SparseFeatures | DenseFeatures), optional - Explicit user features. - item_features : (SparseFeatures | DenseFeatures), optional - Explicit item features. - verbose : int - Whether to print output. - - Returns - ------- - user_factors : np.ndarray - Combined latent and explicit user factors. - item_factors : np.ndarray - Combined latent and explicit user factors. - """ - n_users, n_items = ui_csr.shape - - # Prepare explicit factors - user_explicit_factors: np.ndarray - if user_features is None: - user_explicit_factors = np.array([]).reshape((n_users, 0)) - else: - user_explicit_factors = user_features.get_dense() - n_user_explicit_factors = user_explicit_factors.shape[1] - - item_explicit_factors: np.ndarray - if item_features is None: - item_explicit_factors = np.array([]).reshape((n_items, 0)) - else: - item_explicit_factors = item_features.get_dense() - n_item_explicit_factors = item_explicit_factors.shape[1] - - # Fix number of factors - n_factors_all = model.factors + n_user_explicit_factors + n_item_explicit_factors - if isinstance(model, GpuAlternatingLeastSquares) and n_factors_all % 32: # pragma: no cover - padding = 32 - n_factors_all % 32 - warnings.warn( - "GPU training requires number of factors to be a multiple of 32." - f" Increasing factors from {n_factors_all} to {n_factors_all + padding}" - f" (increasing latent factors from {model.factors} to {model.factors + padding})" - ) - n_latent_factors = model.factors + padding - else: - n_latent_factors = model.factors - n_factors_all = n_latent_factors + n_user_explicit_factors + n_item_explicit_factors - model.factors = n_factors_all - - # Prepare latent factors - random_state = check_random_state(model.random_state) - user_latent_factors = random_state.rand(n_users, n_latent_factors) * 0.01 - item_latent_factors = random_state.rand(n_items, n_latent_factors) * 0.01 - - # Prepare paired factors - user_factors_paired_to_items = np.zeros((n_users, n_item_explicit_factors)) - item_factors_paired_to_users = np.zeros((n_items, n_user_explicit_factors)) - - # Make full factors - user_factors = np.hstack( - ( - user_explicit_factors, - user_latent_factors, - user_factors_paired_to_items, - ) - ).astype(model.dtype) - item_factors = np.hstack( - ( - item_factors_paired_to_users, - item_latent_factors, - item_explicit_factors, - ) - ).astype(model.dtype) - - ui_csr = ui_csr.astype(np.float32) if isinstance(model, GpuAlternatingLeastSquares): # pragma: no cover - _fit_combined_factors_on_gpu_inplace( - model, - ui_csr, - user_factors, - item_factors, - n_user_explicit_factors, - n_item_explicit_factors, - verbose, - ) + features_model = GpuAlternatingLeastSquares(**feaures_model_params) else: - _fit_combined_factors_on_cpu_inplace( - model, - ui_csr, - user_factors, - item_factors, - n_user_explicit_factors, - n_item_explicit_factors, - verbose, - ) - - return user_factors, item_factors - - -def _fit_combined_factors_on_cpu_inplace( - model: AlternatingLeastSquares, - ui_csr: sparse.csr_matrix, - user_factors: np.ndarray, - item_factors: np.ndarray, - n_user_explicit_factors: int, - n_item_explicit_factors: int, - verbose: int, -) -> None: - n_factors = user_factors.shape[1] - user_explicit_factors = user_factors[:, :n_user_explicit_factors].copy() - item_explicit_factors = item_factors[:, n_factors - n_item_explicit_factors :].copy() - iu_csr = ui_csr.T.tocsr(copy=False) - - for _ in tqdm(range(model.iterations), disable=verbose == 0): - model.solver( - ui_csr, - user_factors, - item_factors, - model.regularization, - model.num_threads, - ) - user_factors[:, :n_user_explicit_factors] = user_explicit_factors - - model.solver( - iu_csr, - item_factors, - user_factors, - model.regularization, - model.num_threads, - ) - item_factors[:, n_factors - n_item_explicit_factors :] = item_explicit_factors - - -def _fit_combined_factors_on_gpu_inplace( - model: AlternatingLeastSquares, - ui_csr: sparse.csr_matrix, - user_factors: np.ndarray, - item_factors: np.ndarray, - n_user_explicit_factors: int, - n_item_explicit_factors: int, - verbose: int, -) -> None: # pragma: no cover - try: - from implicit.gpu import ( # pylint: disable=import-outside-toplevel - CuCSRMatrix, - CuDenseMatrix, - CuLeastSquaresSolver, - ) - except ImportError: - raise RuntimeError("implicit.cuda is not available") - - n_factors = user_factors.shape[1] - if n_factors > MAX_GPU_FACTORS: - raise ValueError(f"When using GPU max number of factors is {MAX_GPU_FACTORS}, here is {n_factors} factors") - - user_explicit_factors = user_factors[:, :n_user_explicit_factors].copy() - item_explicit_factors = item_factors[:, n_factors - n_item_explicit_factors :].copy() - iu_csr = ui_csr.T.tocsr(copy=False) - - iu_csr_cuda = CuCSRMatrix(iu_csr) - ui_csr_cuda = CuCSRMatrix(ui_csr) - user_factors_cuda = CuDenseMatrix(user_factors) - item_factors_cuda = CuDenseMatrix(item_factors) - solver = CuLeastSquaresSolver(n_factors) - - for _ in tqdm(range(model.iterations), disable=verbose == 0): - solver.least_squares( - ui_csr_cuda, - user_factors_cuda, - item_factors_cuda, - model.regularization, - model.cg_steps, - ) - user_factors_cuda.to_host(user_factors) - user_factors[:, :n_user_explicit_factors] = user_explicit_factors - user_factors_cuda = CuDenseMatrix(user_factors) - - solver.least_squares( - iu_csr_cuda, - item_factors_cuda, - user_factors_cuda, - model.regularization, - model.cg_steps, - ) - item_factors_cuda.to_host(item_factors) - item_factors[:, n_factors - n_item_explicit_factors :] = item_explicit_factors - item_factors_cuda = CuDenseMatrix(item_factors) - - user_factors_cuda.to_host(user_factors) - item_factors_cuda.to_host(item_factors) + features_model = CpuAlternatingLeastSquares(**feaures_model_params) + features_model.item_factors = y_factors + features_model.fit(xy_csr) + return features_model.user_factors \ No newline at end of file From e0c038fada1c1b495f7d322138b558b3fa354e8e Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Wed, 4 Oct 2023 00:10:00 +0300 Subject: [PATCH 06/18] fixed Matrix format for features in GPU als --- rectools/models/implicit_als.py | 51 ++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/rectools/models/implicit_als.py b/rectools/models/implicit_als.py index c8a8c4c6..767360d2 100644 --- a/rectools/models/implicit_als.py +++ b/rectools/models/implicit_als.py @@ -17,8 +17,8 @@ from copy import deepcopy import numpy as np -from implicit.cpu.als import AlternatingLeastSquares as CpuAlternatingLeastSquares -from implicit.gpu.als import AlternatingLeastSquares as GpuAlternatingLeastSquares +from implicit.cpu.als import AlternatingLeastSquares as CPUAlternatingLeastSquares +from implicit.gpu.als import AlternatingLeastSquares as GPUAlternatingLeastSquares from implicit.utils import check_random_state from scipy import sparse from tqdm.auto import tqdm @@ -30,7 +30,7 @@ MAX_GPU_FACTORS = 1024 AVAILABLE_RECOMMEND_METHODS = ("loop",) -AlternatingLeastSquares = tp.Union[CpuAlternatingLeastSquares, GpuAlternatingLeastSquares] +AlternatingLeastSquares = tp.Union[CPUAlternatingLeastSquares, GPUAlternatingLeastSquares] class ImplicitALSWrapperModel(VectorModel): @@ -61,12 +61,13 @@ def __init__(self, model: AlternatingLeastSquares, verbose: int = 0, fit_feature if fit_features_together: raise NotImplementedError("We temporarily do not support fitting features together") - if isinstance(model, GpuAlternatingLeastSquares) and model.factors > MAX_GPU_FACTORS: # pragma: no cover + if isinstance(model, GPUAlternatingLeastSquares) and model.factors > MAX_GPU_FACTORS: # pragma: no cover raise ValueError(f"When using GPU max number of factors is {MAX_GPU_FACTORS}") self.model: AlternatingLeastSquares self._model = model # for refit; TODO: try to do it better self.fit_features_together = fit_features_together + self.use_gpu = isinstance(model, GPUAlternatingLeastSquares) def _fit(self, dataset: Dataset) -> None: # type: ignore self.model = deepcopy(self._model) @@ -83,14 +84,28 @@ def _fit(self, dataset: Dataset) -> None: # type: ignore self.verbose, ) + if self.use_gpu: + user_factors = Matrix(user_factors) + item_factors = Matrix(item_factors) + self.model.user_factors = user_factors self.model.item_factors = item_factors + def _get_users_vectors(self, model: AlternatingLeastSquares) -> np.ndarray: + if self.use_gpu: + return model.user_factors.to_numpy() + return model.user_factors + + def _get_items_vectors(self, model: AlternatingLeastSquares) -> np.ndarray: + if self.use_gpu: + return model.items_factors.to_numpy() + return model.items_factors + def _get_users_factors(self, dataset: Dataset) -> Factors: - return Factors(self.model.user_factors) + return Factors(self._get_users_vectors(self.model)) def _get_items_factors(self, dataset: Dataset) -> Factors: - return Factors(self.model.item_factors) + return Factors(self._get_items_vectors(self.model)) def get_vectors(self) -> tp.Tuple[np.ndarray, np.ndarray]: """ @@ -104,7 +119,7 @@ def get_vectors(self) -> tp.Tuple[np.ndarray, np.ndarray]: """ if not self.is_fitted: raise NotFittedError(self.__class__.__name__) - return self.model.user_factors, self.model.item_factors + return self._get_users_vectors(self.model), self._get_items_vectors(self.model) def fit_als_with_features_separately( @@ -140,8 +155,8 @@ def fit_als_with_features_separately( iu_csr = ui_csr.T.tocsr(copy=False) model.fit(ui_csr, show_progress=verbose > 0) - user_factors_chunks = [model.user_factors] - item_factors_chunks = [model.item_factors] + user_factors_chunks = [self.get_users_vectors(model)] + item_factors_chunks = [self.get_items_vectors(model)] if user_features is not None: user_feature_factors = user_features.get_dense() @@ -161,7 +176,7 @@ def fit_als_with_features_separately( def _fit_paired_factors(model: AlternatingLeastSquares, xy_csr: sparse.csr_matrix, y_factors: np.ndarray) -> np.ndarray: - feaures_model_params = dict( + features_model_params = dict( factors=y_factors.shape[1], regularization=model.regularization, alpha=model.alpha, @@ -169,10 +184,14 @@ def _fit_paired_factors(model: AlternatingLeastSquares, xy_csr: sparse.csr_matri iterations=1, random_state=model.random_state ) - if isinstance(model, GpuAlternatingLeastSquares): # pragma: no cover - features_model = GpuAlternatingLeastSquares(**feaures_model_params) + if self.use_gpu: # pragma: no cover + features_model = GPUAlternatingLeastSquares(**features_model_params) + features_model.item_factors = Matrix(y_factors) + features_model.fit(xy_csr) + x_factors = features_model.user_factors.to_numpy() else: - features_model = CpuAlternatingLeastSquares(**feaures_model_params) - features_model.item_factors = y_factors - features_model.fit(xy_csr) - return features_model.user_factors \ No newline at end of file + features_model = CPUAlternatingLeastSquares(**features_model_params) + features_model.item_factors = y_factors + features_model.fit(xy_csr) + x_factors = features_model.user_factors + return x_factors \ No newline at end of file From 5f728d1eabd46afaa8c6d3209fa3a86fee47eb76 Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Wed, 4 Oct 2023 00:43:01 +0300 Subject: [PATCH 07/18] linters --- rectools/models/implicit_als.py | 104 ++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/rectools/models/implicit_als.py b/rectools/models/implicit_als.py index 767360d2..823bd1b9 100644 --- a/rectools/models/implicit_als.py +++ b/rectools/models/implicit_als.py @@ -13,15 +13,13 @@ # limitations under the License. import typing as tp -import warnings from copy import deepcopy import numpy as np from implicit.cpu.als import AlternatingLeastSquares as CPUAlternatingLeastSquares +import implicit.gpu from implicit.gpu.als import AlternatingLeastSquares as GPUAlternatingLeastSquares -from implicit.utils import check_random_state from scipy import sparse -from tqdm.auto import tqdm from rectools.dataset import Dataset, Features from rectools.exceptions import NotFittedError @@ -75,37 +73,27 @@ def _fit(self, dataset: Dataset) -> None: # type: ignore if self.fit_features_together: raise NotImplementedError("We temporarily do not support fitting features together") - else: - user_factors, item_factors = fit_als_with_features_separately( - self.model, - ui_csr, - dataset.user_features, - dataset.item_features, - self.verbose, - ) + + user_factors, item_factors = fit_als_with_features_separately( + self.model, + ui_csr, + dataset.user_features, + dataset.item_features, + self.verbose, + ) if self.use_gpu: - user_factors = Matrix(user_factors) - item_factors = Matrix(item_factors) + user_factors = implicit.gpu.Matrix(user_factors) + item_factors = implicit.gpu.Matrix(item_factors) self.model.user_factors = user_factors self.model.item_factors = item_factors - def _get_users_vectors(self, model: AlternatingLeastSquares) -> np.ndarray: - if self.use_gpu: - return model.user_factors.to_numpy() - return model.user_factors - - def _get_items_vectors(self, model: AlternatingLeastSquares) -> np.ndarray: - if self.use_gpu: - return model.items_factors.to_numpy() - return model.items_factors - def _get_users_factors(self, dataset: Dataset) -> Factors: - return Factors(self._get_users_vectors(self.model)) + return Factors(get_users_vectors(self.model)) def _get_items_factors(self, dataset: Dataset) -> Factors: - return Factors(self._get_items_vectors(self.model)) + return Factors(get_items_vectors(self.model)) def get_vectors(self) -> tp.Tuple[np.ndarray, np.ndarray]: """ @@ -119,7 +107,45 @@ def get_vectors(self) -> tp.Tuple[np.ndarray, np.ndarray]: """ if not self.is_fitted: raise NotFittedError(self.__class__.__name__) - return self._get_users_vectors(self.model), self._get_items_vectors(self.model) + return get_users_vectors(self.model), get_items_vectors(self.model) + + +def get_users_vectors(model: AlternatingLeastSquares) -> np.ndarray: + """ + Get users vectors from ALS model as numpy array + + Parameters + ---------- + model : AlternatingLeastSquares + Model to get vectors from. Can be CPU or GPU model + + Returns + ------- + np.ndarray + User vectors + """ + if isinstance(model, GPUAlternatingLeastSquares): + return model.user_factors.to_numpy() + return model.user_factors + + +def get_items_vectors(model: AlternatingLeastSquares) -> np.ndarray: + """ + Get items vectors from ALS model as numpy array + + Parameters + ---------- + model : AlternatingLeastSquares + Model to get vectors from. Can be CPU or GPU model + + Returns + ------- + np.ndarray + Item vectors + """ + if isinstance(model, GPUAlternatingLeastSquares): + return model.item_factors.to_numpy() + return model.item_factors def fit_als_with_features_separately( @@ -155,8 +181,8 @@ def fit_als_with_features_separately( iu_csr = ui_csr.T.tocsr(copy=False) model.fit(ui_csr, show_progress=verbose > 0) - user_factors_chunks = [self.get_users_vectors(model)] - item_factors_chunks = [self.get_items_vectors(model)] + user_factors_chunks = [get_users_vectors(model)] + item_factors_chunks = [get_items_vectors(model)] if user_features is not None: user_feature_factors = user_features.get_dense() @@ -176,17 +202,17 @@ def fit_als_with_features_separately( def _fit_paired_factors(model: AlternatingLeastSquares, xy_csr: sparse.csr_matrix, y_factors: np.ndarray) -> np.ndarray: - features_model_params = dict( - factors=y_factors.shape[1], - regularization=model.regularization, - alpha=model.alpha, - dtype=model.dtype, - iterations=1, - random_state=model.random_state - ) - if self.use_gpu: # pragma: no cover + features_model_params = { + "factors": y_factors.shape[1], + "regularization": model.regularization, + "alpha": model.alpha, + "dtype": model.dtype, + "iterations": 1, + "random_state": model.random_state, + } + if isinstance(model, GPUAlternatingLeastSquares): # pragma: no cover features_model = GPUAlternatingLeastSquares(**features_model_params) - features_model.item_factors = Matrix(y_factors) + features_model.item_factors = implicit.gpu.Matrix(y_factors) features_model.fit(xy_csr) x_factors = features_model.user_factors.to_numpy() else: @@ -194,4 +220,4 @@ def _fit_paired_factors(model: AlternatingLeastSquares, xy_csr: sparse.csr_matri features_model.item_factors = y_factors features_model.fit(xy_csr) x_factors = features_model.user_factors - return x_factors \ No newline at end of file + return x_factors From 6e7aa13a359a4b9e8c75cc491e2e5075596583fc Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Wed, 4 Oct 2023 13:19:55 +0300 Subject: [PATCH 08/18] updated changelog --- CHANGELOG.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a0d05a4..37e55b2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ### Added +- Added `MRR (Mean Reciprocal Rank)` to `metrics` ([#29](https://github.com/MobileTeleSystems/RecTools/pull/29)) +- Added `F1beta`, `MCC (Matthew correlation coefficient)` to `metrics` ([#32](https://github.com/MobileTeleSystems/RecTools/pull/32)) +- Added `LastNSplitter` to `model_selection` ([#33](https://github.com/MobileTeleSystems/RecTools/pull/32)) +- Added random `KFoldSplitter` to `model_selection` ([#31](https://github.com/MobileTeleSystems/RecTools/pull/31)) +- + +### Changed +- Bumped `implicit` version to 0.7.1 ([#45](https://github.com/MobileTeleSystems/RecTools/pull/45)) +- Bumped `poetry` version to 1.4.0 ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) +- Updated dependencies in poetry lockfile ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) +- Moved `nmslib` from main dependencies to extras ([#36](https://github.com/MobileTeleSystems/RecTools/pull/36)) +- Added base `Splitter` class to construct data splitters ([#31](https://github.com/MobileTeleSystems/RecTools/pull/31)) +- Updated notebooks in `examples` ([#44](https://github.com/MobileTeleSystems/RecTools/pull/44)) + +### Fixed +- fixed bugs with new version of `pytorch_lightning` ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) +- fixed `pylint` config for new version ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) +- fixed CI ([#40](https://github.com/MobileTeleSystems/RecTools/pull/40)) ([#34](https://github.com/MobileTeleSystems/RecTools/pull/34)) +- fixed cyclic imports ([#45](https://github.com/MobileTeleSystems/RecTools/pull/45)) -- Mean Reciprocal Rank metric +### Removed +- Temporarily removed support for fitting ALS model with features together ([#45](https://github.com/MobileTeleSystems/RecTools/pull/45)) From 3ac9d7a75657948eefb1f293f1eee6346817ecfd Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Wed, 4 Oct 2023 13:20:31 +0300 Subject: [PATCH 09/18] fixed als tests --- rectools/models/implicit_als.py | 2 +- tests/models/test_implicit_als.py | 43 +++++++++++++++---------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/rectools/models/implicit_als.py b/rectools/models/implicit_als.py index 823bd1b9..408f0036 100644 --- a/rectools/models/implicit_als.py +++ b/rectools/models/implicit_als.py @@ -15,9 +15,9 @@ import typing as tp from copy import deepcopy +import implicit.gpu import numpy as np from implicit.cpu.als import AlternatingLeastSquares as CPUAlternatingLeastSquares -import implicit.gpu from implicit.gpu.als import AlternatingLeastSquares as GPUAlternatingLeastSquares from scipy import sparse diff --git a/tests/models/test_implicit_als.py b/tests/models/test_implicit_als.py index 0c157e8e..3f88e4b9 100644 --- a/tests/models/test_implicit_als.py +++ b/tests/models/test_implicit_als.py @@ -14,16 +14,18 @@ import typing as tp +import implicit.gpu import numpy as np import pandas as pd import pytest -from implicit.als import AlternatingLeastSquares +from implicit.als import AlternatingLeastSquares as AlternatingLeastSquaresFactory from implicit.gpu import HAS_CUDA from rectools import Columns from rectools.dataset import Dataset, DenseFeatures, IdMap, Interactions, SparseFeatures from rectools.exceptions import NotFittedError from rectools.models import ImplicitALSWrapperModel +from rectools.models.implicit_als import AlternatingLeastSquares, GPUAlternatingLeastSquares from rectools.models.utils import recommend_from_scores from .data import DATASET @@ -39,8 +41,15 @@ def _init_model_factors_inplace(model: AlternatingLeastSquares, dataset: Dataset n_factors = model.factors n_users = dataset.user_id_map.to_internal.size n_items = dataset.item_id_map.to_internal.size - model.user_factors = np.linspace(0.1, 0.5, n_users * n_factors).reshape(n_users, n_factors) - model.item_factors = np.linspace(0.1, 0.5, n_items * n_factors).reshape(n_items, n_factors) + user_factors = np.linspace(0.1, 0.5, n_users * n_factors).reshape(n_users, n_factors) + item_factors = np.linspace(0.1, 0.5, n_items * n_factors).reshape(n_items, n_factors) + + if isinstance(model, GPUAlternatingLeastSquares): + user_factors = implicit.gpu.Matrix(user_factors) + item_factors = implicit.gpu.Matrix(item_factors) + + model.user_factors = user_factors + model.item_factors = item_factors @pytest.fixture def dataset(self) -> Dataset: @@ -71,7 +80,7 @@ def dataset(self) -> Dataset: ), ), ) - @pytest.mark.parametrize("fit_features_together", (True, False)) + @pytest.mark.parametrize("fit_features_together", (False,)) # return True option after def test_basic( self, dataset: Dataset, @@ -80,7 +89,7 @@ def test_basic( expected: pd.DataFrame, use_gpu: bool, ) -> None: - base_model = AlternatingLeastSquares(factors=2, num_threads=2, iterations=100) + base_model = AlternatingLeastSquaresFactory(factors=2, num_threads=2, iterations=100) self._init_model_factors_inplace(base_model, dataset) model = ImplicitALSWrapperModel(model=base_model, fit_features_together=fit_features_together).fit(dataset) actual = model.recommend( @@ -115,7 +124,7 @@ def test_with_whitelist( expected: tp.Dict[int, tp.Set[int]], use_gpu: bool, ) -> None: - base_model = AlternatingLeastSquares(factors=32, num_threads=2, use_gpu=use_gpu) + base_model = AlternatingLeastSquaresFactory(factors=32, num_threads=2, use_gpu=use_gpu) model = ImplicitALSWrapperModel(model=base_model).fit(dataset) actual = model.recommend( users=np.array([10, 20]), @@ -129,7 +138,7 @@ def test_with_whitelist( @pytest.mark.parametrize("filter_viewed", (True, False)) def test_raises_when_new_user(self, dataset: Dataset, filter_viewed: bool, use_gpu: bool) -> None: - base_model = AlternatingLeastSquares(factors=2, num_threads=2, random_state=1, use_gpu=use_gpu) + base_model = AlternatingLeastSquaresFactory(factors=2, num_threads=2, random_state=1, use_gpu=use_gpu) model = ImplicitALSWrapperModel(model=base_model).fit(dataset) with pytest.raises(KeyError): model.recommend( @@ -142,16 +151,6 @@ def test_raises_when_new_user(self, dataset: Dataset, filter_viewed: bool, use_g @pytest.mark.parametrize( "fit_features_together,expected", ( - ( - True, - pd.DataFrame( - { - Columns.User: ["u1", "u1", "u2", "u3", "u3"], - Columns.Item: ["i2", "i4", "i4", "i3", "i2"], - Columns.Rank: [1, 2, 1, 1, 2], - } - ), - ), ( False, pd.DataFrame( @@ -186,7 +185,7 @@ def test_happy_path_with_features(self, fit_features_together: bool, expected: p dataset = Dataset(user_id_map, item_id_map, interactions, user_features, item_features) # In case of big number of iterations there are differences between CPU and GPU results - base_model = AlternatingLeastSquares(factors=32, num_threads=2, use_gpu=use_gpu) + base_model = AlternatingLeastSquaresFactory(factors=32, num_threads=2, use_gpu=use_gpu) self._init_model_factors_inplace(base_model, dataset) # Make common number of factors 32, so that CPU and GPU results be equal if fit_features_together: @@ -206,7 +205,7 @@ def test_happy_path_with_features(self, fit_features_together: bool, expected: p ) def test_get_vectors(self, dataset: Dataset, use_gpu: bool) -> None: - base_model = AlternatingLeastSquares(use_gpu=use_gpu) + base_model = AlternatingLeastSquaresFactory(use_gpu=use_gpu) model = ImplicitALSWrapperModel(model=base_model).fit(dataset) user_embeddings, item_embeddings = model.get_vectors() predictions = user_embeddings @ item_embeddings.T @@ -224,7 +223,7 @@ def test_get_vectors(self, dataset: Dataset, use_gpu: bool) -> None: np.testing.assert_almost_equal(vectors_scores, reco_scores, decimal=5) def test_raises_when_get_vectors_from_not_fitted(self, use_gpu: bool) -> None: - model = ImplicitALSWrapperModel(model=AlternatingLeastSquares(use_gpu=use_gpu)) + model = ImplicitALSWrapperModel(model=AlternatingLeastSquaresFactory(use_gpu=use_gpu)) with pytest.raises(NotFittedError): model.get_vectors() @@ -274,7 +273,7 @@ def test_i2i( expected: pd.DataFrame, use_gpu: bool, ) -> None: - base_model = AlternatingLeastSquares(factors=2, iterations=100, num_threads=2, use_gpu=use_gpu) + base_model = AlternatingLeastSquaresFactory(factors=2, iterations=100, num_threads=2, use_gpu=use_gpu) self._init_model_factors_inplace(base_model, dataset) model = ImplicitALSWrapperModel(model=base_model).fit(dataset) actual = model.recommend_to_items( @@ -291,6 +290,6 @@ def test_i2i( ) def test_second_fit_refits_model(self, use_gpu: bool, dataset: Dataset) -> None: - base_model = AlternatingLeastSquares(factors=8, num_threads=2, use_gpu=use_gpu, random_state=1) + base_model = AlternatingLeastSquaresFactory(factors=8, num_threads=2, use_gpu=use_gpu, random_state=1) model = ImplicitALSWrapperModel(model=base_model) assert_second_fit_refits_model(model, dataset) From a1aad08d4eb0a849d9bf65c0557cdc58aa7e34da Mon Sep 17 00:00:00 2001 From: Daria <93913290+blondered@users.noreply.github.com> Date: Wed, 4 Oct 2023 13:24:30 +0300 Subject: [PATCH 10/18] fixed poetry version in README.md Co-authored-by: Emiliy Feldman --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7eec5f64..a47680a2 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ To install all requirements run ``` make install ``` -You must have `python3` and `poetry>=1.4.0` installed. +You must have `python3` and `poetry==1.4.0` installed. For autoformatting run ``` From 533149e09f8f5d610b292a65262998c783733bc7 Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Wed, 4 Oct 2023 13:27:00 +0300 Subject: [PATCH 11/18] fixed implicit version in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 77bc5bce..729b271a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ numpy = ">=1.19.5, <2.0.0" pandas = ">=0.25.3, <2.0.0" scipy = "^1.5.4" tqdm = "^4.27.0" -implicit = "0.7.1" +implicit = "^0.7.1" attrs = ">=19.1.0,<22.0.0" typeguard = "^2.0.1" lightfm = "1.17" From b881797bbc70efd8673f8c990adb7729658c9a8e Mon Sep 17 00:00:00 2001 From: Daria <93913290+blondered@users.noreply.github.com> Date: Wed, 4 Oct 2023 15:52:36 +0300 Subject: [PATCH 12/18] Update CHANGELOG.md Co-authored-by: Emiliy Feldman --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37e55b2e..e60f36a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Bumped `implicit` version to 0.7.1 ([#45](https://github.com/MobileTeleSystems/RecTools/pull/45)) -- Bumped `poetry` version to 1.4.0 ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) +- Bumped `poetry` version to 1.4.0 for github workflows ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) - Updated dependencies in poetry lockfile ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) - Moved `nmslib` from main dependencies to extras ([#36](https://github.com/MobileTeleSystems/RecTools/pull/36)) - Added base `Splitter` class to construct data splitters ([#31](https://github.com/MobileTeleSystems/RecTools/pull/31)) From 46ed63593dca207ebf7c8e0d16498fd676b47f3a Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Wed, 4 Oct 2023 16:16:59 +0300 Subject: [PATCH 13/18] fixed dtype in als test --- tests/models/test_implicit_als.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/models/test_implicit_als.py b/tests/models/test_implicit_als.py index 3f88e4b9..7f00d309 100644 --- a/tests/models/test_implicit_als.py +++ b/tests/models/test_implicit_als.py @@ -41,8 +41,8 @@ def _init_model_factors_inplace(model: AlternatingLeastSquares, dataset: Dataset n_factors = model.factors n_users = dataset.user_id_map.to_internal.size n_items = dataset.item_id_map.to_internal.size - user_factors = np.linspace(0.1, 0.5, n_users * n_factors).reshape(n_users, n_factors) - item_factors = np.linspace(0.1, 0.5, n_items * n_factors).reshape(n_items, n_factors) + user_factors = np.linspace(0.1, 0.5, n_users * n_factors, dtype=np.float32).reshape(n_users, n_factors) + item_factors = np.linspace(0.1, 0.5, n_items * n_factors, dtype=np.float32).reshape(n_items, n_factors) if isinstance(model, GPUAlternatingLeastSquares): user_factors = implicit.gpu.Matrix(user_factors) From 75937fb84589c4e646e3084df312a00ffc9ff85b Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Wed, 4 Oct 2023 16:22:48 +0300 Subject: [PATCH 14/18] updated lock file --- poetry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 70b6d082..d4e66c3f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2372,4 +2372,4 @@ nn = ["pytorch-lightning", "torch"] [metadata] lock-version = "2.0" python-versions = ">=3.7.2, <3.10.0" -content-hash = "6e044af6b03abda7bbae6ee66bef5a06eee97f3798b96c728ed8931b53915589" +content-hash = "d892c9fa41680849827e12d3175695b046210aac6bb0905d3a752429cf131291" From ea0a035dd5a25c3aacd9a7c52bed2f3c8e0e464d Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Wed, 4 Oct 2023 16:30:33 +0300 Subject: [PATCH 15/18] minor comment fixes --- CHANGELOG.md | 10 +++++----- tests/models/test_implicit_als.py | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e60f36a7..ef46f040 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,16 +17,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Bumped `implicit` version to 0.7.1 ([#45](https://github.com/MobileTeleSystems/RecTools/pull/45)) - Bumped `poetry` version to 1.4.0 for github workflows ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) -- Updated dependencies in poetry lockfile ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) +- Updated dependencies ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) - Moved `nmslib` from main dependencies to extras ([#36](https://github.com/MobileTeleSystems/RecTools/pull/36)) - Added base `Splitter` class to construct data splitters ([#31](https://github.com/MobileTeleSystems/RecTools/pull/31)) - Updated notebooks in `examples` ([#44](https://github.com/MobileTeleSystems/RecTools/pull/44)) ### Fixed -- fixed bugs with new version of `pytorch_lightning` ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) -- fixed `pylint` config for new version ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) -- fixed CI ([#40](https://github.com/MobileTeleSystems/RecTools/pull/40)) ([#34](https://github.com/MobileTeleSystems/RecTools/pull/34)) -- fixed cyclic imports ([#45](https://github.com/MobileTeleSystems/RecTools/pull/45)) +- Fixed bugs with new version of `pytorch_lightning` ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) +- Fixed `pylint` config for new version ([#43](https://github.com/MobileTeleSystems/RecTools/pull/43)) +- Fixed CI ([#40](https://github.com/MobileTeleSystems/RecTools/pull/40)) ([#34](https://github.com/MobileTeleSystems/RecTools/pull/34)) +- Fixed cyclic imports ([#45](https://github.com/MobileTeleSystems/RecTools/pull/45)) ### Removed - Temporarily removed support for fitting ALS model with features together ([#45](https://github.com/MobileTeleSystems/RecTools/pull/45)) diff --git a/tests/models/test_implicit_als.py b/tests/models/test_implicit_als.py index 7f00d309..fc00a7c9 100644 --- a/tests/models/test_implicit_als.py +++ b/tests/models/test_implicit_als.py @@ -80,7 +80,8 @@ def dataset(self) -> Dataset: ), ), ) - @pytest.mark.parametrize("fit_features_together", (False,)) # return True option after + # `True` option for `fit_features_together` must be added after we develop support for it + @pytest.mark.parametrize("fit_features_together", (False,)) def test_basic( self, dataset: Dataset, From 6bc42eea4d4195d277d82e26fe3d991c20bfca62 Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Wed, 4 Oct 2023 16:38:10 +0300 Subject: [PATCH 16/18] refactor AlternatingLeastSquares confused classes --- rectools/models/implicit_als.py | 22 +++++++++++----------- tests/models/test_implicit_als.py | 22 +++++++++++----------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/rectools/models/implicit_als.py b/rectools/models/implicit_als.py index 408f0036..f88dade8 100644 --- a/rectools/models/implicit_als.py +++ b/rectools/models/implicit_als.py @@ -28,7 +28,7 @@ MAX_GPU_FACTORS = 1024 AVAILABLE_RECOMMEND_METHODS = ("loop",) -AlternatingLeastSquares = tp.Union[CPUAlternatingLeastSquares, GPUAlternatingLeastSquares] +AnyAlternatingLeastSquares = tp.Union[CPUAlternatingLeastSquares, GPUAlternatingLeastSquares] class ImplicitALSWrapperModel(VectorModel): @@ -40,7 +40,7 @@ class ImplicitALSWrapperModel(VectorModel): Parameters ---------- - model : AlternatingLeastSquares + model : AnyAlternatingLeastSquares Base model that will be used. verbose : int, default 0 Degree of verbose output. If 0, no output will be provided. @@ -53,7 +53,7 @@ class ImplicitALSWrapperModel(VectorModel): u2i_dist = Distance.DOT i2i_dist = Distance.COSINE - def __init__(self, model: AlternatingLeastSquares, verbose: int = 0, fit_features_together: bool = False): + def __init__(self, model: AnyAlternatingLeastSquares, verbose: int = 0, fit_features_together: bool = False): super().__init__(verbose=verbose) if fit_features_together: @@ -61,7 +61,7 @@ def __init__(self, model: AlternatingLeastSquares, verbose: int = 0, fit_feature if isinstance(model, GPUAlternatingLeastSquares) and model.factors > MAX_GPU_FACTORS: # pragma: no cover raise ValueError(f"When using GPU max number of factors is {MAX_GPU_FACTORS}") - self.model: AlternatingLeastSquares + self.model: AnyAlternatingLeastSquares self._model = model # for refit; TODO: try to do it better self.fit_features_together = fit_features_together @@ -110,13 +110,13 @@ def get_vectors(self) -> tp.Tuple[np.ndarray, np.ndarray]: return get_users_vectors(self.model), get_items_vectors(self.model) -def get_users_vectors(model: AlternatingLeastSquares) -> np.ndarray: +def get_users_vectors(model: AnyAlternatingLeastSquares) -> np.ndarray: """ Get users vectors from ALS model as numpy array Parameters ---------- - model : AlternatingLeastSquares + model : AnyAlternatingLeastSquares Model to get vectors from. Can be CPU or GPU model Returns @@ -129,13 +129,13 @@ def get_users_vectors(model: AlternatingLeastSquares) -> np.ndarray: return model.user_factors -def get_items_vectors(model: AlternatingLeastSquares) -> np.ndarray: +def get_items_vectors(model: AnyAlternatingLeastSquares) -> np.ndarray: """ Get items vectors from ALS model as numpy array Parameters ---------- - model : AlternatingLeastSquares + model : AnyAlternatingLeastSquares Model to get vectors from. Can be CPU or GPU model Returns @@ -149,7 +149,7 @@ def get_items_vectors(model: AlternatingLeastSquares) -> np.ndarray: def fit_als_with_features_separately( - model: AlternatingLeastSquares, + model: AnyAlternatingLeastSquares, ui_csr: sparse.csr_matrix, user_features: tp.Optional[Features], item_features: tp.Optional[Features], @@ -160,7 +160,7 @@ def fit_als_with_features_separately( Parameters ---------- - model: AlternatingLeastSquares + model: AnyAlternatingLeastSquares Base model to fit. ui_csr : sparse.csr_matrix Matrix of interactions. @@ -201,7 +201,7 @@ def fit_als_with_features_separately( return user_factors, item_factors -def _fit_paired_factors(model: AlternatingLeastSquares, xy_csr: sparse.csr_matrix, y_factors: np.ndarray) -> np.ndarray: +def _fit_paired_factors(model: AnyAlternatingLeastSquares, xy_csr: sparse.csr_matrix, y_factors: np.ndarray) -> np.ndarray: features_model_params = { "factors": y_factors.shape[1], "regularization": model.regularization, diff --git a/tests/models/test_implicit_als.py b/tests/models/test_implicit_als.py index fc00a7c9..94808375 100644 --- a/tests/models/test_implicit_als.py +++ b/tests/models/test_implicit_als.py @@ -18,14 +18,14 @@ import numpy as np import pandas as pd import pytest -from implicit.als import AlternatingLeastSquares as AlternatingLeastSquaresFactory +from implicit.als import AlternatingLeastSquares from implicit.gpu import HAS_CUDA from rectools import Columns from rectools.dataset import Dataset, DenseFeatures, IdMap, Interactions, SparseFeatures from rectools.exceptions import NotFittedError from rectools.models import ImplicitALSWrapperModel -from rectools.models.implicit_als import AlternatingLeastSquares, GPUAlternatingLeastSquares +from rectools.models.implicit_als import AnyAlternatingLeastSquares, GPUAlternatingLeastSquares from rectools.models.utils import recommend_from_scores from .data import DATASET @@ -36,7 +36,7 @@ @pytest.mark.parametrize("use_gpu", (False, True) if HAS_CUDA else (False,)) class TestImplicitALSWrapperModel: @staticmethod - def _init_model_factors_inplace(model: AlternatingLeastSquares, dataset: Dataset) -> None: + def _init_model_factors_inplace(model: AnyAlternatingLeastSquares, dataset: Dataset) -> None: """Init factors to make the test deterministic""" n_factors = model.factors n_users = dataset.user_id_map.to_internal.size @@ -90,7 +90,7 @@ def test_basic( expected: pd.DataFrame, use_gpu: bool, ) -> None: - base_model = AlternatingLeastSquaresFactory(factors=2, num_threads=2, iterations=100) + base_model = AlternatingLeastSquares(factors=2, num_threads=2, iterations=100) self._init_model_factors_inplace(base_model, dataset) model = ImplicitALSWrapperModel(model=base_model, fit_features_together=fit_features_together).fit(dataset) actual = model.recommend( @@ -125,7 +125,7 @@ def test_with_whitelist( expected: tp.Dict[int, tp.Set[int]], use_gpu: bool, ) -> None: - base_model = AlternatingLeastSquaresFactory(factors=32, num_threads=2, use_gpu=use_gpu) + base_model = AlternatingLeastSquares(factors=32, num_threads=2, use_gpu=use_gpu) model = ImplicitALSWrapperModel(model=base_model).fit(dataset) actual = model.recommend( users=np.array([10, 20]), @@ -139,7 +139,7 @@ def test_with_whitelist( @pytest.mark.parametrize("filter_viewed", (True, False)) def test_raises_when_new_user(self, dataset: Dataset, filter_viewed: bool, use_gpu: bool) -> None: - base_model = AlternatingLeastSquaresFactory(factors=2, num_threads=2, random_state=1, use_gpu=use_gpu) + base_model = AlternatingLeastSquares(factors=2, num_threads=2, random_state=1, use_gpu=use_gpu) model = ImplicitALSWrapperModel(model=base_model).fit(dataset) with pytest.raises(KeyError): model.recommend( @@ -186,7 +186,7 @@ def test_happy_path_with_features(self, fit_features_together: bool, expected: p dataset = Dataset(user_id_map, item_id_map, interactions, user_features, item_features) # In case of big number of iterations there are differences between CPU and GPU results - base_model = AlternatingLeastSquaresFactory(factors=32, num_threads=2, use_gpu=use_gpu) + base_model = AlternatingLeastSquares(factors=32, num_threads=2, use_gpu=use_gpu) self._init_model_factors_inplace(base_model, dataset) # Make common number of factors 32, so that CPU and GPU results be equal if fit_features_together: @@ -206,7 +206,7 @@ def test_happy_path_with_features(self, fit_features_together: bool, expected: p ) def test_get_vectors(self, dataset: Dataset, use_gpu: bool) -> None: - base_model = AlternatingLeastSquaresFactory(use_gpu=use_gpu) + base_model = AlternatingLeastSquares(use_gpu=use_gpu) model = ImplicitALSWrapperModel(model=base_model).fit(dataset) user_embeddings, item_embeddings = model.get_vectors() predictions = user_embeddings @ item_embeddings.T @@ -224,7 +224,7 @@ def test_get_vectors(self, dataset: Dataset, use_gpu: bool) -> None: np.testing.assert_almost_equal(vectors_scores, reco_scores, decimal=5) def test_raises_when_get_vectors_from_not_fitted(self, use_gpu: bool) -> None: - model = ImplicitALSWrapperModel(model=AlternatingLeastSquaresFactory(use_gpu=use_gpu)) + model = ImplicitALSWrapperModel(model=AlternatingLeastSquares(use_gpu=use_gpu)) with pytest.raises(NotFittedError): model.get_vectors() @@ -274,7 +274,7 @@ def test_i2i( expected: pd.DataFrame, use_gpu: bool, ) -> None: - base_model = AlternatingLeastSquaresFactory(factors=2, iterations=100, num_threads=2, use_gpu=use_gpu) + base_model = AlternatingLeastSquares(factors=2, iterations=100, num_threads=2, use_gpu=use_gpu) self._init_model_factors_inplace(base_model, dataset) model = ImplicitALSWrapperModel(model=base_model).fit(dataset) actual = model.recommend_to_items( @@ -291,6 +291,6 @@ def test_i2i( ) def test_second_fit_refits_model(self, use_gpu: bool, dataset: Dataset) -> None: - base_model = AlternatingLeastSquaresFactory(factors=8, num_threads=2, use_gpu=use_gpu, random_state=1) + base_model = AlternatingLeastSquares(factors=8, num_threads=2, use_gpu=use_gpu, random_state=1) model = ImplicitALSWrapperModel(model=base_model) assert_second_fit_refits_model(model, dataset) From 2a8d7affd06cb3a2f337f088a1745e205846657e Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Wed, 4 Oct 2023 16:46:37 +0300 Subject: [PATCH 17/18] linters --- README.md | 2 +- rectools/models/implicit_als.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a47680a2..f1fe544a 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ You must have `python3` and `poetry==1.4.0` installed. For autoformatting run ``` -make autoformat +make format ``` For linters check run diff --git a/rectools/models/implicit_als.py b/rectools/models/implicit_als.py index f88dade8..048bfdf7 100644 --- a/rectools/models/implicit_als.py +++ b/rectools/models/implicit_als.py @@ -201,7 +201,9 @@ def fit_als_with_features_separately( return user_factors, item_factors -def _fit_paired_factors(model: AnyAlternatingLeastSquares, xy_csr: sparse.csr_matrix, y_factors: np.ndarray) -> np.ndarray: +def _fit_paired_factors( + model: AnyAlternatingLeastSquares, xy_csr: sparse.csr_matrix, y_factors: np.ndarray +) -> np.ndarray: features_model_params = { "factors": y_factors.shape[1], "regularization": model.regularization, From cec82e028586ef8e5dc40e5fe4f55f59f51f399a Mon Sep 17 00:00:00 2001 From: Daria Tikhonovich Date: Wed, 4 Oct 2023 17:02:46 +0300 Subject: [PATCH 18/18] fixed coverage --- rectools/models/implicit_als.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rectools/models/implicit_als.py b/rectools/models/implicit_als.py index 048bfdf7..0fe4259b 100644 --- a/rectools/models/implicit_als.py +++ b/rectools/models/implicit_als.py @@ -82,7 +82,7 @@ def _fit(self, dataset: Dataset) -> None: # type: ignore self.verbose, ) - if self.use_gpu: + if self.use_gpu: # pragma: no cover user_factors = implicit.gpu.Matrix(user_factors) item_factors = implicit.gpu.Matrix(item_factors) @@ -124,7 +124,7 @@ def get_users_vectors(model: AnyAlternatingLeastSquares) -> np.ndarray: np.ndarray User vectors """ - if isinstance(model, GPUAlternatingLeastSquares): + if isinstance(model, GPUAlternatingLeastSquares): # pragma: no cover return model.user_factors.to_numpy() return model.user_factors @@ -143,7 +143,7 @@ def get_items_vectors(model: AnyAlternatingLeastSquares) -> np.ndarray: np.ndarray Item vectors """ - if isinstance(model, GPUAlternatingLeastSquares): + if isinstance(model, GPUAlternatingLeastSquares): # pragma: no cover return model.item_factors.to_numpy() return model.item_factors