paint-brush
Ham vs Spam៖ របៀបកំណត់ និងចាត់ថ្នាក់អ៊ីមែល Spamដោយ@ramakadapala
ប្រវត្តិសាស្ត្រថ្មី។

Ham vs Spam៖ របៀបកំណត់ និងចាត់ថ្នាក់អ៊ីមែល Spam

ដោយ Rama7m2025/03/08
Read on Terminal Reader

យូរ​ពេក; អាន

នៅក្នុងគម្រោងនេះ ឧបករណ៍រៀនម៉ាស៊ីនមួយចំនួនត្រូវបានប្រើដើម្បីមើលពីរបៀបដែលពួកគេអនុវត្តជាអ្នកចាត់ថ្នាក់សម្រាប់អ៊ីមែល Ham និង Spam ។ ការពិពណ៌នាអំពីការគណនាត្រូវបានបង្ហាញ ក៏ដូចជាការប្រៀបធៀបនៃការអនុវត្តរបស់ពួកគេ។
featured image - Ham vs Spam៖ របៀបកំណត់ និងចាត់ថ្នាក់អ៊ីមែល Spam
Rama HackerNoon profile picture

អរូបី

អ៊ីមែលគឺជាឧបករណ៍ដ៏សំខាន់សម្រាប់ការទំនាក់ទំនងនៅក្នុងពិភពលោកបច្ចុប្បន្ន។ ទោះយ៉ាងណាក៏ដោយ អ៊ីមែល spam បានលេចចេញជាបញ្ហាប្រឈមដ៏សំខាន់មួយ។ សារដែលមិនមានការស្នើសុំទាំងនេះពីប្រភពមិនស្គាល់ច្រើនតែបំពេញប្រអប់ទទួល ដែលរំខានដល់ការទំនាក់ទំនង និងផលិតភាព។ ឯកសារនេះស៊ើបអង្កេតការប្រើប្រាស់បច្ចេកទេសរៀនម៉ាស៊ីនផ្សេងៗដើម្បីចាត់ថ្នាក់អ៊ីមែលជា "សារឥតបានការ" ឬ "ហាំ" (មិនមែនសារឥតបានការ)។


គំរូចំណាត់ថ្នាក់ដូចជា K-Nearest Neighbors (KNN), Logistic Regression, Support Vector Machines (SVM) និង Naïve Bayes ត្រូវបានវាយតម្លៃដោយប្រៀបធៀបប្រសិទ្ធភាពរបស់ពួកគេក្នុងការចាត់ថ្នាក់អ៊ីមែល។ ដំណើរការនៃម៉ូដែលនីមួយៗត្រូវបានវាយតម្លៃដោយផ្អែកលើរង្វាស់ដូចជាភាពត្រឹមត្រូវ ភាពជាក់លាក់ ការរំលឹកឡើងវិញ និងពិន្ទុ F1 ដើម្បីកំណត់វិធីសាស្រ្តណាមួយដែលសមស្របបំផុតសម្រាប់កិច្ចការនេះ។

សេចក្តីផ្តើម

នៅឆ្នាំ 2023 មានអ៊ីម៉ែលចំនួន 347.3 ពាន់លានដែលត្រូវបានផ្ញើជារៀងរាល់ថ្ងៃ ដែលក្នុងនោះ អ៊ីម៉ែល spam បង្កើតបាន 45% នៃចរាចរអ៊ីម៉ែលទាំងអស់។ សារឥតបានការអ៊ីម៉ែលទាំងនេះធ្វើឱ្យអាជីវកម្មចំណាយអស់ 20.5 ពាន់លានដុល្លារជារៀងរាល់ឆ្នាំ។ អាស្រ័យហេតុនេះ វានឹងតែងតែមានតម្រូវការដើម្បីធានាថាសារឥតបានការត្រូវបានចាត់ថ្នាក់យ៉ាងត្រឹមត្រូវ និងមិនជ្រៀតជ្រែកជាមួយចរាចរណ៍អ៊ីមែលស្របច្បាប់។ នេះគឺជាវិធីដ៏មានប្រយោជន៍មួយដើម្បីយល់ពីសក្តានុពល និងការអនុវត្តនៃការរៀនម៉ាស៊ីន។


ក្នុងករណីនៃការរកឃើញសារឥតបានការ អ្នកប្រើប្រាស់ដែលស្គាល់អ៊ីមែលអាចកំណត់បានយ៉ាងងាយស្រួលនូវសារឥតបានការស្ទើរតែភ្លាមៗនៅពេលមើលវា។ ជាលទ្ធផល ខ្ញុំជឿថា ការងារនេះបញ្ជាក់ថា ការកំណត់អត្តសញ្ញាណសារឥតបានការ គឺជាកម្មវិធីដ៏មានសារៈប្រយោជន៍សម្រាប់អ្នកចាត់ថ្នាក់រៀនម៉ាស៊ីន។ ដើម្បីចាត់ថ្នាក់អ៊ីមែលជាសារឥតបានការ ឬ ham មានជំហានដំណើរការមុនជាច្រើនដែលពាក់ព័ន្ធ - រៀបចំទិន្នន័យដើម្បីអាចទទួលយកបានចំពោះអ្នកចាត់ថ្នាក់លីនេអ៊ែរ បន្ទាប់មកកំណត់សញ្ញាសម្ងាត់ និងកំណត់បន្ទាត់នីមួយៗដើម្បីបំប្លែងទៅជាតារាង TF-IDF (Term Frequency – Inverse Document Frequency)។


KNN ជាមួយ K=3 ត្រូវបានជ្រើសរើសជាគំរូមូលដ្ឋាន។ ការតំរែតំរង់ផ្នែកឡូជីស្ទិកជាមួយនឹងការធ្វើឱ្យទៀងទាត់ L1 ម៉ូដែល Naïve Bayes និងម៉ូដែល SVM ក៏ត្រូវបានសាកល្បងផងដែរមុនពេលសម្រេចចិត្តម៉ូដែលល្អបំផុត។ សម្រាប់គោលបំណងនៃគម្រោងនេះ សំណុំទិន្នន័យពី Kaggle ត្រូវបានប្រើប្រាស់ ដែលមានឯកសារអ៊ីមែល "ham" ចំនួន 2551 និងឯកសារអ៊ីម៉ែលសារឥតបានការចំនួន 501 ហើយការធ្វើគំរូត្រូវបានធ្វើឡើងដោយប្រើភាសាសរសេរកម្មវិធី R ។

ជិតនៃពាក្យ hamការបិទជិតនៃពាក្យសារឥតបានការ វិធីសាស្រ្តធ្វើតាម

គំនូសតាងខាងក្រោមនឹងជួយឱ្យយល់អំពីលំហូរនៃជំហានផ្សេងៗដែលបានធ្វើតាម -

រូបភាពបង្ហាញពីជំហានគម្រោង

ជំហានដំណើរការមុន។

ដោយសារទិន្នន័យអត្ថបទជាច្រើនកំពុងត្រូវបានប្រើប្រាស់ ចាំបាច់ត្រូវដំណើរការទិន្នន័យជាមុន ដើម្បីសម្អាតវា ហើយយកវាទៅជាទម្រង់ដែលអាចប្រើបានដោយគំរូចំណាត់ថ្នាក់។ ខាងក្រោមនេះនឹងពន្យល់ពីដំណើរការមួយជំហានម្តង ៗ ដែលត្រូវបានអនុវត្តជាផ្នែកនៃដំណើរការទិន្នន័យជាមុន -

  • ទិន្នន័យអត្ថបទត្រូវតែអាចទទួលយកបានចំពោះអ្នកចាត់ថ្នាក់លីនេអ៊ែរ។ នេះមានន័យថាសំណុំទិន្នន័យត្រូវតែត្រូវបានបំប្លែងដោយប្រើវិធីសាស្ត្រទាញយកមុខងារអត្ថបទទៅជាលក្ខណៈលេខ។

  • ជាដំបូង បន្ទាត់នីមួយៗនៃអត្ថបទត្រូវបានសម្គាល់ និងកំណត់ទៅជាទម្រង់ខាងក្រោម។ ដំណើរការ stemming ធ្វើឱ្យខ្លីពាក្យដោយយកចុងបញ្ចប់ដែលមិនប៉ះពាល់។ ឧទាហរណ៍ កម្មករក្លាយជាកម្មករក្នុងឧទាហរណ៍ខាងក្រោម។

    ពាក្យនិងពាក្យដើមរបស់ពួកគេ។

  • បន្ទាប់មក ទិន្នន័យដែលបានកំណត់អត្តសញ្ញាណត្រូវបានបំប្លែងទៅជាតារាង TF-IDF (ប្រេកង់ពាក្យ - ប្រេកង់ឯកសារបញ្ច្រាស)។ TF-IDF គឺជាវិធីសាស្រ្តមួយសម្រាប់ការវិភាគអត្ថបទដែលចែងអំពី n-gram នីមួយៗនៅក្នុងឯកសារមួយទាក់ទងនឹងប្រេកង់ឯកសារ-inverse frequency របស់វា។ ប្រេកង់ពាក្យគឺគ្រាន់តែជាប្រេកង់នៃពាក្យដែលបានផ្តល់ឱ្យនៅក្នុងឯកសារ (ក្នុងករណីនេះ អ៊ីមែល) ។ ប្រេកង់ឯកសារបញ្ច្រាសត្រូវបានបញ្ជាក់ជាទូទៅដូចជា៖

    កំណត់ហេតុ ((ចំនួនសរុបនៃឯកសារ/ចំនួនឯកសារដែលមានរយៈពេល)


វាបម្រើដើម្បីថ្លឹងថ្លែងពាក្យសំខាន់ៗច្រើនជាងពាក្យញឹកញាប់ ហើយដូច្នេះលក្ខខណ្ឌមិនសូវសំខាន់។

  • បន្ទាប់មក លក្ខខណ្ឌត្រូវបានកាត់បន្ថយដោយគ្រាន់តែជ្រើសរើសលក្ខខណ្ឌទាំងនោះដែលបានបង្ហាញខ្លួននៅក្នុងឯកសារយ៉ាងហោចណាស់ 2 ភាគរយ ប៉ុន្តែមិនលើសពី 95 ភាគរយនៃឯកសារនោះទេ។ ដំណើរការនេះការពារការបំពេញលើសដោយធានាថាលក្ខខណ្ឌត្រូវបានដកចេញ ដែលមានលក្ខណៈប្លែកពេក ឬរីករាលដាលពេកនៅក្នុងឈុតបណ្តុះបណ្តាល។


  • នេះផ្តល់លទ្ធផលសរុបចំនួន 1,130 ពាក្យនៅក្នុងសំណុំទិន្នន័យបណ្តុះបណ្តាល។


វាជាការសំខាន់ក្នុងការកត់សម្គាល់ថាយុទ្ធសាស្រ្តសម្រាប់ការប្រើប្រាស់ TF-IDF ជាមួយនឹងសំណុំសាកល្បងនឹងអនុវត្តតែការបណ្តុះបណ្តាលគំរូនៅលើការបណ្តុះបណ្តាល TF-IDF ហើយបន្ទាប់មកដើម្បីគណនាឡើងវិញនូវ TF-IDF ជាមួយនឹងទិន្នន័យពេញលេញសម្រាប់ភាពត្រឹមត្រូវនៃការធ្វើតេស្ត។ វាត្រូវបានទាមទារ ពីព្រោះ TF-IDF ពឹងផ្អែកលើប្រេកង់នៅក្នុងសំណុំទិន្នន័យ ទាំងមូល នៃពាក្យជាក់លាក់មួយ ហើយក៏មិនរួមបញ្ចូលទិន្នន័យសាកល្បងក្នុងអំឡុងពេលបណ្តុះបណ្តាលផងដែរ។


ជាចុងក្រោយ មានតែលក្ខខណ្ឌកំពូល 1000 ដោយប្រេកង់ពាក្យទាំងមូលប៉ុណ្ណោះដែលត្រូវបានជ្រើសរើសនៅក្នុងសំណុំបណ្តុះបណ្តាល។


តារាង tf-idf

កូដសម្រាប់ជំហានដំណើរការមុន -

 #tokenize word_tokens <- complete_tbl %>% unnest_tokens(word,content) #stemming word_tokens<-word_tokens %>% mutate(word_stem=SnowballC::wordStem(word)) #remove any words with numbers word_tokens <- word_tokens[-grep('^\\d+$', word_tokens$word_stem),] #remove any words with . word_tokens <- word_tokens[-grep('[.]', word_tokens$word_stem),] #remove any single character words word_tokens <- word_tokens[-grep('.\\b[az]\\b.', word_tokens$word_stem),] #remove tokens which match stop words word_tokens <- word_tokens %>% filter(!word %in% stopWords) word_tokens <- word_tokens %>% filter(!word_stem %in% stopWords) #split into training and test word_tokens_train <- word_tokens %>% filter(document %in% ind) #create tfidf for training and then a complete tfidf for testing tfidf_train<-word_tokens_train %>% count(document,word_stem,sort=TRUE) %>% bind_tf_idf(word_stem,document,n) tfidf_complete<-word_tokens %>% count(document,word_stem,sort=TRUE) %>% bind_tf_idf(word_stem,document,n)

ម៉ូដែលដែលបានប្រើ

  1. K-Nearest Neighbors (KNN) - គំរូមូលដ្ឋាន

ម៉ូដែលនេះសម្រេចបាននូវភាពជាក់លាក់ដែលសមហេតុផល ប៉ុន្តែមានភាពរសើបខ្លាំង ដែលមានន័យថា hams ជាច្រើនត្រូវបានព្យាករណ៍ថាជាសារឥតបានការ។ ចំណាំថានៅក្នុងលទ្ធផលនីមួយៗខាងក្រោម ថ្នាក់វិជ្ជមានគឺ "ham" ។ នេះ​គឺ​ជា​លទ្ធផល​ដ៏​អាក្រក់​បំផុត​សម្រាប់​អ្នក​ប្រើ​ដែល​ពួកគេ​នឹង​ខក​ខាន​អ៊ីមែល​ពិត​ជា​ច្រើន​ដែល​ត្រូវ​បាន​ចាត់​ថ្នាក់​ជា​សារ​ឥត​បានការ។


ម៉ាទ្រីសច្របូកច្របល់សម្រាប់ KNN ជាមួយ K=3
លេខកូដសម្រាប់ម៉ូដែល KNN -

 ##train a model library(e1071) library(caret) library(class) library(LiblineaR) ##remove document number since this is indicative of spam or ham wide_feat_train<-subset(wide_feat_train, select=-c(document)) wide_feat_test<-subset(wide_feat_test,select=-c(document)) #Base model is a knn attempt knn_pred<-knn(train=wide_feat_train,test=wide_feat_test,cl=labels_train$label,k=3) knn_results<-confusionMatrix(knn_pred,labels_test$label) knn_results knn_results$byClass["F1"] knn_results$byClass["Precision"] knn_results$byClass["Recall"]
  1. តំរែតំរង់ផ្នែកដឹកជញ្ជូន

បន្ទាប់ពីទទួលបានលទ្ធផលមិនល្អពីគំរូ KNN Logistic Regression គឺជាគំរូបន្ទាប់ដែលត្រូវបានប្រើប្រាស់។


សម្រាប់គោលបំណងនៃសេណារីយ៉ូនេះ,

រូបមន្តហាងឆេងសម្រាប់ Ham និងអ៊ីម៉ែលសារឥតបានការ

ការតំរែតំរង់ផ្នែកឡូជីស្ទិកត្រូវបានអនុវត្តជាមួយប៉ារ៉ាម៉ែត្រខ្ពស់ខាងក្រោម -

  • ការបាត់បង់ = "L1"
  • តម្លៃ = ២
  • epsilon = 0.1


គំរូនេះផ្តល់នូវលទ្ធផលដូចខាងក្រោមនៅលើសំណុំទិន្នន័យសាកល្បង ដែលជាការកែលម្អយ៉ាងសំខាន់លើគំរូ KNN រួចហើយ។ ភាពត្រឹមត្រូវសរុបគឺខ្ពស់ណាស់ ប៉ុន្តែភាពជាក់លាក់បង្ហាញថា វានៅតែមានកន្លែងខ្លះសម្រាប់កែលម្អ។ អ្នកប្រើប្រាស់នៃគំរូនេះនឹងរកឃើញ Hams មួយចំនួនដែលត្រូវបានព្យាករណ៍ថាជា Spams ។
ម៉ាទ្រីសច្របូកច្របល់សម្រាប់ការតំរែតំរង់ផ្នែកភស្តុភារ កូដសម្រាប់គំរូតំរែតំរង់ផ្នែកដឹកជញ្ជូន -

 #Next is a logistic regression usin the below hyperparameters grid_logit <- expand.grid(loss="L1",cost=2,epsilon=0.1) lr <- train(x=wide_feat_train,y=labels_train$label,method="regLogistic",tuneGrid=grid_logit) lr_results<-confusionMatrix(as.factor(predict(lr,wide_feat_test)),labels_test$label) lr_results p_lr = predict(lr,wide_feat_test) prednum_lr<-ifelse(p_lr=="spam",0,1) roc_lr<-roc(labels_test$label,prednum_lr) plot(roc_lr) roc_lr$auc p1_lr<- prediction(as.numeric(p_lr),as.numeric(labels_test$label)) pr_lr <- performance(p1_lr, "prec", "rec") plot(pr_lr) lr_results$byClass["F1"] lr_results$byClass["Precision"] lr_results$byClass["Recall"]
  1. ម៉ូដែល Naive Bayes

ម៉ូដែលបន្ទាប់ដែលបានសាកល្បងគឺម៉ូដែល Naïve Bayes ។ សម្រាប់គំរូនេះ ការផ្ទៀងផ្ទាត់ឆ្លងកាត់ត្រូវបានធ្វើឡើងដើម្បីស្វែងរកប៉ារ៉ាម៉ែត្រខ្ពស់ដែលល្អបំផុតជាមួយនឹងវិធីសាស្រ្តប្រាំដង។ លទ្ធផលនេះនៅក្នុងប៉ារ៉ាម៉ែត្រខាងក្រោមសម្រាប់ Naive-Bayes -


  • laplace = 0
  • userkernel = FALSE
  • លៃតម្រូវ = 1


ម៉ូដែលនេះក៏សម្រេចបានលទ្ធផលល្អទាំងភាពជាក់លាក់ និងភាពប្រែប្រួលផងដែរ។

ម៉ាទ្រីសច្របូកច្របល់សម្រាប់គំរូ Naïve Bayes លេខកូដសម្រាប់ម៉ូដែល Naive Bayes -

 ##naive bayes main model nb_cv <- train( x=wide_feat_train, y=labels_train$label, method = "naive_bayes", trControl = train_control, tuneGrid = grid ) nb <- naiveBayes(wide_feat_train,labels_train$label,adjust=1,laplace=0,usekernel=FALSE) nb_results<-confusionMatrix(as.factor(predict(nb,wide_feat_test)),labels_test$label) nb_results p = predict(nb,wide_feat_test) prednum<-ifelse(p=="spam",0,1) roc_nb<-roc(labels_test$label,prednum) plot(roc_nb) roc_nb$auc p1<- prediction(as.numeric(p),as.numeric(labels_test$label)) pr <- performance(p1, "prec", "rec") plot(pr) nb_results$byClass["F1"] nb_results$byClass["Precision"] nb_results$byClass["Recall"]


4. គាំទ្រម៉ាស៊ីនវ៉ិចទ័រ (SVM)

គំរូចុងក្រោយគឺ SVM ឆ្លងកាត់សុពលភាពជាមួយខឺណែលលីនេអ៊ែរ។ គាំទ្រម៉ាស៊ីនវ៉ិចទ័រ ព្យាយាមស្វែងរកយន្តហោះខ្ពស់ដែលបំបែកជាអតិបរមា ដើម្បីបំបែកទិន្នន័យរវាងថ្នាក់ពីរ។


នៅទីនេះ CV 5 ដងត្រូវបានអនុវត្តដោយប្រើ R Library caret ដើម្បីកំណត់អត្តសញ្ញាណ hyperparameters ល្អបំផុត។ ប៉ារ៉ាម៉ែត្រខ្ពស់ទាំងនេះត្រូវបានបង្ហាញខាងក្រោម -

  • តម្លៃ = ១
  • ការបាត់បង់ = L2
  • ទម្ងន់ = ៣


លទ្ធផលនៃគំរូនេះនៅពេលអនុវត្តទៅសំណុំទិន្នន័យសាកល្បងដែលទុកចោលត្រូវបានបង្ហាញខាងក្រោម -

ម៉ាទ្រីសច្របូកច្របល់សម្រាប់ SVM

កូដសម្រាប់ SVM -

 #svm train_control <- trainControl( method = "cv", number = 5 ) svm <- train(x=wide_feat_train,y=labels_train$label,method="svmLinearWeights2",trControl=train_control) svm$bestTune svm_results<-confusionMatrix(as.factor(predict(svm,wide_feat_test)),labels_test$label) svm_results p_svm = predict(svm,wide_feat_test) prednum_svm<-ifelse(p_svm=="spam",0,1) roc_svm<-roc(labels_test$label,prednum_svm) plot(roc_svm,colorize=T,lwd=3, main=" ROC curve for SVM model") roc_svm$auc p1_svm<- prediction(as.numeric(p_svm),as.numeric(labels_test$label)) pr <- performance(p1_svm, "prec", "rec") plot(pr) svm_results$byClass["F1"] svm_results$byClass["Precision"] svm_results$byClass["Recall"]

លទ្ធផល

តារាងខាងក្រោមសង្ខេបអំពីវិធានការដែលត្រូវបានពិចារណាសម្រាប់ការជ្រើសរើសគំរូល្អបំផុត-

គំរូ

ភាពត្រឹមត្រូវ

ពិន្ទុ F1

ភាពជាក់លាក់

រំលឹកឡើងវិញ

ខេ.អិន

០.២៥២

០.២២៩៣

០.៨៩៤៧

0.1315

តំរែតំរង់ផ្នែកដឹកជញ្ជូន

០.៩៦២៤

០.៩៧៨១

០.៩៥៩១

០.៩៩៨

Naive Bayes

០.៩៧២២

០.៩៨៣៤

០.៩៨៨២

០.៩៧៨៧

SVM

០.៩៨៨៥

0.9932

០.៩៨៨៦

ពីតារាងខាងលើវាអាចត្រូវបានគេមើលឃើញថា SVM ដំណើរការបានល្អបំផុតបើប្រៀបធៀបទៅនឹងម៉ូដែលផ្សេងទៀត។


ដើម្បីបញ្ជាក់បន្ថែម ខ្សែកោង ROC ត្រូវបានគ្រោងទុក ហើយតម្លៃ AUC ត្រូវបានគណនា។

គំរូ

ខេ.អិន

តំរែតំរង់ផ្នែកដឹកជញ្ជូន

Naïve Bayes

SVM

AUC

.៥២៣២

.៨៨២

.៩៥៧៤

.៩៦២៨

រូប៖ តម្លៃ AUC សម្រាប់ម៉ូដែលទាំង ៤

ពីការវាស់វែងខាងលើ វាអាចត្រូវបានសន្និដ្ឋានថា SVM ដែលមានសុពលភាពឆ្លង 5 ដងដំណើរការល្អបំផុតលើសំណុំទិន្នន័យក្នុងការចាត់ថ្នាក់អ៊ីមែលជា Ham និង spam ។

សេចក្តីសន្និដ្ឋាន

ការត្រងសារឥតបានការនឹងតែងតែជាវាលមួយដែលមានការវិវត្តជាបន្តបន្ទាប់ ខណៈដែលអ្នកផ្ញើសារឥតបានការកំពុងស្វែងរកវិធីសាស្រ្តថ្មីៗ និងប្រកបដោយភាពច្នៃប្រឌិតជានិច្ចដើម្បីផ្ញើសារឥតបានការ។ គ្មានដំណោះស្រាយប្រឆាំងសារឥតបានការតែមួយអាចត្រឹមត្រូវទេ។ នៅក្នុងគម្រោងនេះ ឧបករណ៍រៀនម៉ាស៊ីនមួយចំនួនត្រូវបានប្រើប្រាស់ដើម្បីមើលពីរបៀបដែលពួកគេធ្វើជាអ្នកចាត់ថ្នាក់សម្រាប់អ៊ីមែល Ham និង Spam ។ ការពិពណ៌នាអំពីការគណនាត្រូវបានបង្ហាញ ក៏ដូចជាការប្រៀបធៀបនៃការអនុវត្តរបស់ពួកគេ។


ក្នុងចំណោមម៉ូដែលរៀនម៉ាស៊ីនទាំងបួនដែលត្រូវបានសាកល្បង SVM ត្រូវបានរកឃើញថាល្អបំផុតទាក់ទងនឹងដំណើរការ។ ម៉ូដែល Logistic Regression និង Naïve Bayes ក៏បង្ហាញលទ្ធផលល្អផងដែរ។

L O A D I N G
. . . comments & more!

About Author

Rama HackerNoon profile picture
I am a highly accomplished data science and analytics professional with over 11 years of expertise in financial services

ព្យួរស្លាក

អត្ថបទនេះត្រូវបានបង្ហាញនៅក្នុង...