Crypto News

Build an AI System to Recommend You the Jazziest Pants (Or Any Other Apparel) on the Planet 🩳

Paano kung alam ng iyong online store kung ano ang nais ng isang customer bago nila ginawa?

Karamihan sa mga engine ng rekomendasyon ay tulad ng kapaki -pakinabang ngunit bahagyang mga katulong na clueless: Iminumungkahi nila ang “tanyag” o “magkatulad” na mga item batay sa limitado, hindi napapanahong data. Sila Pakikibaka Kapag ang mga gumagamit ay bago (ang problema sa malamig na pagsisimula), at bihira silang mabilis na umangkop kapag nagbabago ang mga kagustuhan ng isang gumagamit sa real-time.

Ngunit paano kung magagawa ng iyong system talagang isipin Tulad ng isang Merchandiser-Pag-combin ng Static Product Data at Real-Time User Behaviour sa Surface ang mga tamang item Sa tamang oras?

Ang gabay na ito ay naglalakad sa iyo sa pamamagitan ng pagbuo ng isang modernong engine ng rekomendasyon Paggamit Superlink.

(Nais na tumalon nang diretso sa code? Suriin ang bukas na source code sa GitHub dito. Handa nang subukan ang mga sistema ng rekomendasyon para sa iyong sariling kaso ng paggamit? Kumuha ng isang demo dito.)

Maaari ka ring sundin kasama ang tutorial in-browser kasama ang aming colab.

TL; DR:

Karamihan sa mga rekomendasyong e-commerce ay alinman sa masyadong static (batay sa panuntunan) o masyadong black-box (mga modelo ng Opaque ML). Nag-aalok ang Superlink ng isang gitnang landas: nababaluktot, real-time na mga rekomendasyon na maaaring umangkop sa mga gumagamit ng malamig na pagsisimula sa pamamagitan ng pagsasama ng metadata na may live na pag-uugali-lahat nang walang pag-retraining ng mga modelo ng ML.

Pagkamit ng pag -personalize sa kabila ng mga hamon sa pag -embed ng vector

Habang ang mga vector embeddings ay maaaring mapabuti ang mga sistema ng rekomendasyon, ang epektibong pagpapatupad ng mga ito ay nangangailangan ng pagtugon sa maraming mga hamon, kabilang ang:

  • Kalidad at kaugnayan: Ang proseso ng pag -embed ng henerasyon, arkitektura, at data ay dapat na maingat na isaalang -alang.
  • Kalat -kalat at maingay na data: Ang mga pag -embed ay hindi gaanong epektibo kapag mayroon silang hindi kumpleto o maingay na pag -input. Ang kalat-kalat na data ay ang crux ng problema sa malamig na pagsisimula.
  • Scalability: Ang mga mahusay na pamamaraan para sa mga malalaking datasets ay kinakailangan; Kung hindi man, ang latency ay magiging isang isyu.

Hinahayaan ka ng Superlinked na tugunan mo ang mga hamong ito sa pamamagitan ng pagsasama ng lahat ng magagamit na data tungkol sa mga gumagamit at produkto sa mayaman na multimodal vectors. Sa aming halimbawa ng E-commerce Recsys sa ibaba, ginagawa namin ito gamit ang sumusunod na mga elemento ng superlink na library:

  • Min_Max Numero ng mga puwang: Para sa pag -unawa sa mga pagsusuri sa customer at impormasyon sa pagpepresyo
  • Text-Similarity Space: Para sa semantikong pag -unawa sa impormasyon ng produkto
  • Mga Kaganapan Schema at Mga epekto Upang baguhin ang mga vectors
  • Mga Timbang ng Oras ng Query – Upang tukuyin kung paano mo nais na tratuhin ang data kapag pinatakbo mo ang query, hayaan kang mag-optimize at mag-scale nang hindi muling pag-embed ang buong dataset (latency)

Sa pamamagitan ng pag-embed ng aming una na kalat-kalat na data na tukoy ng gumagamit (ang paunang kagustuhan ng produkto ng gumagamit), maaari naming hawakan ang problema sa malamig na pagsisimula. Bilang accrues ng pag -uugali ng gumagamit, maaari pa tayong lumakad nang higit pa, Hyper-Personal na mga rekomendasyon sa pamamagitan ng pag -embed ng data ng kaganapang ito, na lumilikha ng isang feedback loop na nagbibigay -daan sa iyo na i -update ang mga vectors na may mga kagustuhan ng gumagamit sa real time. Bilang karagdagan, ang mga timbang ng oras ng query ng Superlink ay hayaan mong maayos ang iyong mga resulta ng pagkuha, biasing ang mga ito upang tumugma sa mga tiyak na kagustuhan ng gumagamit.

Magsimula tayo!

Ang pagtatayo ng isang engine ng rekomendasyon ng e-commerce na may superlink

Sa pagsisimula ng pag -unlad, mayroon kaming mga sumusunod data ng produkto:

Mayroon din tayong sumusunod data tungkol sa mga gumagamit at produkto:

  1. Ang bawat gumagamit ay pumili ng isa sa tatlong mga produkto na inaalok kapag nagrehistro sila (ibig sabihin, data ng kagustuhan ng produkto)

  2. Pag -uugali ng gumagamit (pagkatapos Nagbibigay ng Rehistro) Nagbibigay ng karagdagang data ng kaganapan – Mga Kagustuhan para sa Mga Katangian ng Tekstuwal ng Mga Produkto (Paglalarawan, Pangalan, Kategorya)

Gayundin, sinasabi sa amin ng Classical Economics na, sa average, ang lahat ng mga gumagamit ng Ceteris Paribus ay ginusto ang mga produkto na:

  • mas kaunti ang gastos

  • Magkaroon ng maraming mga pagsusuri

  • may mas mataas na rating

Maaari naming i -set up ang aming mga puwang upang isaalang -alang ang data na ito, upang ang aming mga recsys ay gumagana sa mga senaryo ng malamig na pagsisimula – inirerekomenda ang mga item para sa mga gumagamit na alam natin. Kapag ang aming Recsys ay tumatakbo at tumatakbo, magkakaroon din kami ng data ng pag -uugali: Mag -click ang mga gumagamit sa ilang mga produkto, bumili ng ilang mga produkto, atbp Maaari naming makuha at gamitin ang data ng kaganapang ito upang lumikha ng mga loop ng feedback, pag -update ng aming mga vectors upang ipakita ang mga kagustuhan ng gumagamit at pagpapabuti ng kalidad ng rekomendasyon.

Pag -set up ng Superlink

Una, kailangan nating i -install ang superlink na library at i -import ang mga klase.

%pip install superlinked==6.0.0

import altair as alt
import os
import pandas as pd
import sys


from superlinked.framework.common.embedding.number_embedding import Mode
from superlinked.framework.common.schema.schema import schema
from superlinked.framework.common.schema.event_schema import event_schema
from superlinked.framework.common.schema.schema_object import String, Integer
from superlinked.framework.common.schema.schema_reference import SchemaReference
from superlinked.framework.common.schema.id_schema_object import IdField
from superlinked.framework.common.parser.dataframe_parser import DataFrameParser
from superlinked.framework.dsl.executor.in_memory.in_memory_executor import (
   InMemoryExecutor,
   InMemoryApp,
)
from superlinked.framework.dsl.index.index import Index
from superlinked.framework.dsl.index.effect import Effect
from superlinked.framework.dsl.query.param import Param
from superlinked.framework.dsl.query.query import Query
from superlinked.framework.dsl.source.in_memory_source import InMemorySource
from superlinked.framework.dsl.space.text_similarity_space import TextSimilaritySpace
from superlinked.framework.dsl.space.number_space import NumberSpace


alt.renderers.enable(get_altair_renderer())
pd.set_option("display.max_colwidth", 190)

Tinukoy din namin ang aming mga datasets, at lumikha ng isang pare -pareho para sa pag -iimbak ng nangungunang 10 mga item – tingnan ang Cell 3 sa notebook.

Ngayon na ang naka -install na aklatan, na -import ng mga klase, at natukoy na mga lokasyon, maaari naming tingnan ang aming mga dataset upang ipaalam sa paraan ng pag -set up ng aming mga puwang. Sa una, mayroon kaming data mula sa pagpaparehistro ng gumagamit – ibig sabihin ,. Alin sa tatlong mga produkto ang napili ng user_1 at user_2. Gagamitin namin ang data na ito upang malutas ang problema sa malamig na pagsisimula.

# the user preferences come from the user being prompted to select a product out of 3 - those will be the initial preferences
# this is done in order to give somewhat personalised recommendations
user_df: pd.DataFrame = pd.read_json(USER_DATASET_URL)
user_df

Pref ng produkto ng gumagamit sa pagpaparehistroPref ng produkto ng gumagamit sa pagpaparehistro

Maaari rin kaming mag -set up ng isang malapit na pagsusuri ng data ng pamamahagi ng aming mga produkto – tingnan ang Cell 5. Nagbibigay ito sa iyo ng larawan kung gaano karaming mga produkto ang nasa iba't ibang mga puntos ng presyo, may iba't ibang mga bilang ng pagsusuri, at may iba't ibang mga rating (kabilang ang kung saan ang karamihan ng mga produkto ay namamalagi sa mga saklaw na ito).

Bilang ng mga produkto kumpara sa presyo, bilang ng pagsusuri, at mga pamamahagi ng ratingBilang ng mga produkto kumpara sa presyo, bilang ng pagsusuri, at mga pamamahagi ng rating

Ang mga bins ng presyo para sa mga produkto ay karamihan sa ibaba ng $ 1000 na punto ng presyo. Maaaring naisin naming itakda ang saklaw ng puwang sa 25-1000 upang gawin itong kinatawan, na hindi maihahatid ng mga mas malalakas na halaga. Ang mga bilang ng pagsusuri ng mga produkto ay pantay na ipinamamahagi, at ang mga rating ng pagsusuri na medyo pantay na ipinamamahagi, kaya hindi kinakailangan ang karagdagang paggamot. Tingnan ang Mga Cell 7-9.

Ang library ng Superlinked ay naglalaman ng isang hanay ng mga pangunahing bloke ng gusali na ginagamit namin upang mabuo ang index at pamahalaan ang pagkuha. Maaari mong basahin ang tungkol sa mga bloke ng gusali na ito nang mas detalyado dito.

Ilagay natin ang mga bloke ng gusali ng aklatan na gagamitin sa aming ECOMM Recsys. Una kailangan mo Tukuyin ang iyong schema Upang sabihin sa system ang tungkol sa iyong data.

# schema is the way to describe the input data flowing into our system - in a typed manner
@schema
class ProductSchema:
   description: String
   name: String
   category: String
   price: Integer
   review_count: Integer
   review_rating: Integer
   id: IdField

@schema
class UserSchema:
   preference_description: String
   preference_name: String
   preference_category: String
   id: IdField

@event_schema
class EventSchema:
   product: SchemaReference[ProductSchema]
   user: SchemaReference[UserSchema]
   event_type: String
   id: IdField

# we instantiate schemas as follows
product = ProductSchema()
user = UserSchema()
event = EventSchema()

Susunod, gumagamit ka ng mga puwang upang sabihin kung paano mo nais na tratuhin ang bawat bahagi ng data kapag nag -embed. Sa mga kahulugan ng espasyo, inilalarawan namin kung paano i -embed ang mga input upang maipakita nila ang mga semantikong relasyon sa aming data. Ang bawat puwang ay na -optimize upang mai -embed ang data upang maibalik ang pinakamataas na posibleng kalidad ng mga resulta ng pagkuha. Aling mga puwang ang ginagamit ay nakasalalay sa iyong datatype.

# textual inputs are embedded in a text similarity space powered by a sentence_transformers model
description_space = TextSimilaritySpace(
   text=[user.preference_description, product.description],
   model="sentence-transformers/all-distilroberta-v1",
)
name_space = TextSimilaritySpace(
   text=[user.preference_name, product.name],
   model="sentence-transformers/all-distilroberta-v1",
)
category_space = TextSimilaritySpace(
   text=[user.preference_category, product.category],
   model="sentence-transformers/all-distilroberta-v1",
)

# NumberSpaces encode numeric input in special ways to reflect a relationship
# here we express relationships to price (lower the better), or ratings and review counts (more/higher the better)
price_space = NumberSpace(
   number=product.price, mode=Mode.MINIMUM, min_value=25, max_value=1000
)
review_count_space = NumberSpace(
   number=product.review_count, mode=Mode.MAXIMUM, min_value=0, max_value=100
)
review_rating_space = NumberSpace(
   number=product.review_rating, mode=Mode.MAXIMUM, min_value=0, max_value=4
)

# create the index using the defined spaces
product_index = Index(
   spaces=[
       description_space,
       name_space,
       category_space,
       price_space,
       review_count_space,
       review_rating_space,
   ]
)

# parse our data into the schemas - not matching column names can be conformed to schemas using the mapping parameter
product_df_parser = DataFrameParser(schema=product)
user_df_parser = DataFrameParser(
   schema=user, mapping={user.preference_description: "preference_desc"}
)

# setup our application
source_product: InMemorySource = InMemorySource(product, parser=product_df_parser)
source_user: InMemorySource = InMemorySource(user, parser=user_df_parser)
executor: InMemoryExecutor = InMemoryExecutor(
   sources=[source_product, source_user], indices=[product_index]
)
app: InMemoryApp = executor.run()

# load the actual data into our system
source_product.put([products_df])
source_user.put([user_df])

Ngayon na nakuha mo ang iyong data na tinukoy sa mga puwang, handa ka nang maglaro sa iyong data at mai -optimize ang mga resulta. Ipakita muna natin Ano ang magagawa natin nang walang mga kaganapan – Ang aming Cold-Start Solution.

Pag-tackle ng problema sa Cold-Start

Dito, tinukoy namin ang isang query sa gumagamit na naghahanap lamang ng kagustuhan ng vector ng gumagamit. Mayroon kaming control control sa kahalagahan (timbang) ng bawat uri ng pag -input (puwang).

user_query = (
   Query(
       product_index,
       weights={
           description_space: Param("description_weight"),
           name_space: Param("name_weight"),
           category_space: Param("category_weight"),
           price_space: Param("price_weight"),
           review_count_space: Param("review_count_weight"),
           review_rating_space: Param("review_rating_weight"),
       },
   )
   .find(product)
   .with_vector(user, Param("user_id"))
   .limit(Param("limit"))
)

# simple recommendations for our user_1
# these are based only on the initial product the user chose when first entering our site
simple_result = app.query(
   user_query,
   user_id="user_1",
   description_weight=1,
   name_weight=1,
   category_weight=1,
   price_weight=1,
   review_count_weight=1,
   review_rating_weight=1,
   limit=TOP_N,
)

simple_result.to_pandas()

Ang mga resulta ng query na ito ay sumasalamin sa katotohanan na ang User_1 ay pumili ng isang handbag nang una silang nakarehistro sa aming eCOMM site.

Gumagamit 1 Rehistro ng Pagpipilian sa Pagpaparehistro ng UserGumagamit 1 Rehistro ng Pagpipilian sa Pagpaparehistro ng User

Posible ring magrekomenda ng mga produkto sa user_1 na Karaniwan Pag -apela – iyon ay, batay sa kanilang presyo na mababa, at pagkakaroon ng maraming magagandang pagsusuri. Ang aming mga resulta ay sumasalamin ngayon sa parehong pagpipilian ng produkto ng User_1 sa pagpaparehistro at Ang pangkalahatang katanyagan ng mga produkto. (Maaari rin tayong maglaro sa mga timbang na ito upang mag -skew ang mga resulta sa direksyon ng isang puwang o sa iba pa.)

general_result = app.query(
   user_query,
   user_id="user_1",
   description_weight=0,
   name_weight=0,
   category_weight=0,
   price_weight=1,
   review_count_weight=1,
   review_rating_weight=1,
   limit=TOP_N,
)

general_result.to_pandas() 

Pangkalahatang Mga Tampok na Mga Tampok na Mga Tampok na Mga TampokPangkalahatang Mga Tampok na Mga Tampok na Mga Tampok na Mga Tampok

Ang isang bagong paghahanap ng gumagamit ay nagpapakilala ng teksto ng query bilang isang input para sa aming mga resulta ng rekomendasyon – tingnan ang Cell 20.

Sa aming halimbawa ng kaso, naghanap ang user_1 para sa “mga dyaket ng damit ng kababaihan”. Maaari naming mai -optimize ang aming mga resulta sa pamamagitan ng pagbibigay Karagdagang timbang sa puwang ng kategorya (category_weight = 10), upang magrekomenda ng higit pang mga produktong “Women Clothing Jackets”.

women_cat_result = app.query(
   search_query,
   user_id="user_1",
   query_text="women clothing jackets",
   description_weight=1,
   name_weight=1,
   category_weight=10,
   price_weight=1,
   review_count_weight=1,
   review_rating_weight=1,
   limit=TOP_N,
)

women_cat_result.to_pandas()

Ang aming karagdagang kategorya ng pagtimbang ay gumagawa ng mas maraming mga resulta ng damit ng kababaihan.

User 1 query para sa "Mga dyaket ng damit ng kababaihan" Recs.pngUser 1 query para sa "Mga dyaket ng damit ng kababaihan" Recs.png

Maaari rin nating bias ang aming mga rekomendasyon sa mga nangungunang mga produkto (review_rating_weight=5), pagbabalanse ng aming nadagdagan na weighting kategorya. Ang mga resulta ngayon ay sumasalamin sa paunang kagustuhan ng User_1 para sa mga handbag at mga item na sa pangkalahatan ay sikat, habang ang mga produkto na may mababang mga rating ay tinanggal nang buo. Tingnan ang Cell 22.

Paggamit ng data ng mga kaganapan upang lumikha ng mga isinapersonal na karanasan

Mabilis na pasulong sa isang buwan. Ang aming mga gumagamit ay nakipag -ugnay sa aming platform – user_1 higit pa, user_2 mas kaunti. Maaari na nating magamit ang aming mga gumagamit ' data ng pag -uugali (Tingnan sa ibaba), na kinakatawan bilang mga kaganapan:

events_df = (
   pd.read_json(EVENT_DATASET_URL)
   .reset_index()
   .rename(columns={"index": "id"})
   .head(NROWS)
)
events_df = events_df.merge(
   products_df[["id"]], left_on="product", right_on="id", suffixes=("", "r")
).drop("idr", axis=1)
events_df = events_df.assign(created_at=1715439600)

events_df

Mga Kaganapan sa GumagamitMga Kaganapan sa Gumagamit

Timbangin ang mga tiyak na aksyon upang irehistro ang antas ng interes ng gumagamit sa isang partikular na produkto, at ayusin ang pag -setup upang isaalang -alang ang mga kaganapan kapag nagsasagawa ng pagkuha.

event_weights = {
   "clicked_on": 0.2,
   "buy": 1,
   "put_to_cart": 0.5,
   "removed_from_cart": -0.5,
}

# adjust the setup to events
product_index_with_events = Index(
    spaces=[
        description_space,
        category_space,
        name_space,
        price_space,
        review_count_space,
        review_rating_space,
    ],
    effects=[
        Effect(
            description_space,
            event.user,
            event_weight * event.product,
            event.event_type == event_type,
        )
        for event_type, event_weight in event_weights.items()
    ]
    + [
        Effect(
            category_space,
            event.user,
            event_weight * event.product,
            event.event_type == event_type,
        )
        for event_type, event_weight in event_weights.items()
    ]
    + [
        Effect(
            name_space,
            event.user,
            event_weight * event.product,
            event.event_type == event_type,
        )
        for event_type, event_weight in event_weights.items()
    ],
)
event_df_parser: DataFrameParser = DataFrameParser(schema=event)
source_event: InMemorySource = InMemorySource(schema=event, parser=event_df_parser)
executor_with_events: InMemoryExecutor = InMemoryExecutor(
    sources=[source_product, source_user, source_event],
    indices=[product_index_with_events],
)
app_with_events: InMemoryApp = executor_with_events.run()

Lumikha kami ngayon ng isang bagong index upang isaalang -alang ang mga kaganapan sa gumagamit, at pagkatapos ay i -personalize ang mga rekomendasyon sa bawat gumagamit nang naaayon. Kahit na ang mga query batay lamang sa vector ng gumagamit ay mas personalized kaysa sa dati.

# for a new index, all data has to be put into the source again
source_product.put([products_df])
source_user.put([user_df])
source_event.put([events_df])

# a query only searching with the user's vector the preferences are now much more personalised thanks to the events
personalised_query = (
   Query(
       product_index_with_events,
       weights={
           description_space: Param("description_weight"),
           category_space: Param("category_weight"),
           name_space: Param("name_weight"),
           price_space: Param("price_weight"),
           review_count_space: Param("review_count_weight"),
           review_rating_space: Param("review_rating_weight"),
       },
   )
   .find(product)
   .with_vector(user, Param("user_id"))
   .limit(Param("limit"))
)

Maaari nating obserbahan ang epekto ng pagsasama ng mga kaganapan sa aming mga recsys sa pamamagitan ng pagtimbang ng personalization Bahagya lang o mabigat. Una, tingnan natin ang epekto (kumpara sa baseline) ng pagtimbang ng mga puwang na naiimpluwensyahan ng mga ito (data ng pag -uugali).

# with small weight on event-affected spaces, we mainly just alter the results below position 4
general_event_result = app_with_events.query(
   personalised_query,
   user_id="user_1",
   description_weight=1,
   category_weight=1,
   name_weight=1,
   price_weight=1,
   review_count_weight=1,
   review_rating_weight=1,
   limit=TOP_N,
)

general_event_result.to_pandas().join(
   simple_result.to_pandas(), lsuffix="", rsuffix="_base"
)[["description", "id", "description_base", "id_base"]]

Sa napakaliit na timbang na nakalagay sa mga puwang na apektado ng mga kaganapan, napansin namin ang isang pagbabago ngunit higit sa lahat lamang sa huling kalahati ng aming nangungunang 10, kumpara sa mga nakaraang resulta (“ID_BASE”, sa kanan).

Bahagyang timbang na mga puwang na apektado ng mga puwang kumpara sa baselineBahagyang timbang na mga puwang na apektado ng mga puwang kumpara sa baseline

Ngunit kung timbangin namin ang mga puwang na apektado ng kaganapan nang mas mabigat, lumilitaw kami ng ganap na mga item ng nobela sa aming listahan ng mga rekomendasyon.

# with larger weight on the the event-affected spaces, more totally new items appear in the TOP10
event_weighted_result = app_with_events.query(
   personalised_query,
   user_id="user_1",
   query_text="",
   description_weight=5,
   category_weight=1,
   name_weight=1,
   price_weight=1,
   review_count_weight=1,
   review_rating_weight=1,
   limit=TOP_N,
)

event_weighted_result.to_pandas().join(
   simple_result.to_pandas(), lsuffix="", rsuffix="_base"
)[["description", "id", "description_base", "id_base"]]

Higit pang mga mabibigat na timbang na mga puwang na apektado ng mga puwang kumpara sa baselineHigit pang mga mabibigat na timbang na mga puwang na apektado ng mga puwang kumpara sa baseline

Maaari rin nating, gumamit ng mga timbang upang mai -personalize ang aming mga rekomendasyon batay sa isang partikular na pag -uugali ng gumagamit (data ng kaganapan) at Kasabay na unahin ang iba pang mga katangian ng produkto – Halimbawa, presyo (tingnan ang Cell 31).

Konklusyon

Ang pagpapatupad ng ECOMM Recsys ng superlink na library (sa itaas) ay nagpapakita sa iyo kung paano mapagtanto ang kapangyarihan ng mga vector embeddings sa pamamagitan ng pagsasama ng semantikong kahulugan ng mga query ng gumagamit at data ng pag -uugali. Gamit ang aming numero ng Min_max at mga puwang ng pagkakatulad ng teksto, mga schema at epekto ng mga kaganapan, at mga timbang ng oras ng query, maaari mong matugunan ang malamig na pagsisimula, kalidad at kaugnayan, at mga hamon sa scalability ng mga recsys at magbigay ng lubos na tumpak, personal na mga rekomendasyon sa paggawa sa paggawa.

Ngayon ang iyong turn! Subukang ipatupad ang superlink na library sa iyong sarili gamit ang aming notebook.

Subukan ito sa iyong sarili – Kunin ang Code at Demo!

  • 💾 Kunin ang code: Suriin ang buong pagpapatupad sa aming Github Repo dito.Tinidor ito, i -tweak ito, at gawin itong iyong sarili!
  • 🚀 Tingnan ito sa aksyon: Nais mo bang makita ito na nagtatrabaho sa isang real-world setup? Mag -book ng mabilis Demo, at galugarin kung paano Superlink maaaring supercharge ang iyong mga rekomendasyon. Kumuha ng isang demo ngayon!

Ang mga makina ng rekomendasyon ay humuhubog sa paraan ng pagtuklas namin ng nilalaman. Ito man ay tanyag na pantalon, musika, o iba pang mga produkto, Ang paghahanap ng vector ay ang hinaharap—At ngayon mayroon kang mga tool upang mabuo ang iyong sarili.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button

Adblocker Detected

Please consider supporting us by disabling your ad blocker