Blog

  • Tổng quan về Recommendation Systems – Phần 1

    Tổng quan về Recommendation Systems – Phần 1

    Tổng quan về Recommendation Systems – Phần 1

    Recommendation Systems

    Xin chào mọi người, đây là bài đầu tiên của mình về chủ đề Machine Learning và cũng là bài đầu tiên về Series Recommendation Systems này, mặc dù chủ đề này không có liên quan đến công việc của mình nhưng do sở thích hay đi đọc block dạo và cũng muốn thử học hỏi thêm tý kiến thức khác nên mình quyết định viết lên series này vì vậy sẽ có nhiều thiếu sót nên mong các anh em có thể góp ý cho mình , gạch đá như nào mình gom hết về xây nhà, nhà em cũng đang thiếu ít gạch

    Nội dung trong phần này

    Trong phần này mình sẽ giới thiệu chung về hệ thống Recommendation và hai nhóm chính của RS, ở bài viết sau mình sẽ đi chi tiết và những nhóm chính để mọi người có thể có cái nhìn chi tiết nhất, bài đầu này như kiểu món tráng miệng cho anh em thôi.

    Giới thiệu về hệ thống

    Mình tin chắc rằng những anh(chị) em ở đây đã gặp phải 1 trong những trường hợp này thường xuyên trong cuộc sống:

    • Khi xem hết 1 video trên youtube hoặc sắp hết sẽ có 1 loạt các gợi ý các video tiếp theo cho bạn xem kể bạn có là chiếu mới khi xem youtube thì họ vẫn có thể đưa cho bạn 1 list video và tất nhiên sẽ có cái mà bạn thích
    • Đắng lòng hơn là khi bạn lỡ nói hoặc tìm kiếm cái gì đó trên google thì khi bạn sử dụng dịch vụ của Google hay lướt news trên Facebook thì sẽ có hàng loạt các gợi ý về sản phẩm tương tự cho bạn , hoặc lỡ bạn có like 1 vài item nào đó trên nên tảng của họ thì bạn cũng sẽ nhận được 1 rổ gợi ý về các loại item đấy cho bạn
    • Trên các sàn thương mại điện tử thì càng ác liệt hơn khi đây chính là cách mà họ moi tiền trong thẻ tín dụng của bạn, nhất là các ông lớn như Amazon , Shopee , Lazada. Mỗi khi bạn mua 1 sản phẩm nào đó thì họ sẽ yêu cầu bạn đánh giá sản phẩm ý để lấy rating ( cái để phục vụ cho họ để đưa ra gợi ý chính xác nhất ) nếu bạn không Rating thì họ sẽ gửi mail , bật thông báo để đòi bạn bao giờ rating mới thôi thế nên mình toàn rating cho có .

    Còn rất nhiều các ví dụ khác mà hệ thống có khả năng tự gợi ý cho người dùng những sản phẩm họ có thể thích đây chính là lý do vì sao mà anh em ta đi mua đồ trên các trang web hay thương mại điện tử thường vượt quá budget. Bằng cách quảng cáo đúng tim đen của người dùng thì doanh thu cũng tăng lên và những thuật toán đằng sau những ứng dụng này là những thuật toán Machine Learning có cách gọi chung là Recommendation Systems – Hệ thống gợi ý

    Recommemdation Systems là 1 trong những mảng tương đối lớn của Machine Learning và có tuổi đời đã khá lớn khi lần đầu tiên được đề cập trong 1 báo cáo kĩ thuật năm 1990 bởi Jussi Karlgren tại Đại Học Columbia và được triển khai quy mô lớn từ năm 1994 trở đi bởi Jussi Karlgren . RS là 1 giải pháp thay thế cho các thuật toán tìm kiếm ví chúng giúp người dùng có thế thấy được các item mà họ có thể thích kể cả họ có tìm tiếm hay không.

    Các thực thể chính trong hệ thống

    Như anh em đã biết thì để làm việc hay xây dựng lên 1 hệ thống thì điều đầu tiên chúng ta cần xác minh là cần những thành phần nào để tạo ra hệ thống. Nói đến hệ thống gợi ý thì chúng ta cần phải có tối thiểu 2 yếu tố sau:

    • User : Rõ ràng luôn, không có người dùng thì gợi ý cho vong ỏ? các đặc điểm của User thường được sử dụng trong gợi ý kết bạn điển hình là Facebook.
    • Item : Nó lại là 1 điều hiển nhiên luôn vì có người dùng mà k có sản phẩm thì gợi ý cho nhau à? Đúng , nếu không có 1 item cụ thể nào đó mà chỉ có người dùng thì chỉ có thể gợi ý người dùng khác cho nhau và các anh em biết cái ứng dụng nào đang sử dụng rồi đó , chính là Tinder nhưng trong trường hợp này User lại tính là 1 item trong bài toán gợi ý kết bạn hay kết nối với nhau.
    • Rating : Thực ra đây là 1 yếu tố mình nghĩ trong 1 vài hệ thống gợi ý sẽ không cần đến nhưng đa số thì đây là 1 yếu tố rất quan trọng để đưa ra gợi ý chính xác nhất cho người dùng. Vì rating hay feedback rất quan trọng , nó mô tả sự quan tâm của 1 người dùng nào đó đến với 1 item nó đó qua đó có thể đưa ra những gợi ý chính xác nhất cho khách hàng.

    Và sau đây mình sẽ đề cập đến 2 nhóm chính trong RS, tất nhiên trong Recommendation Systems có rất nhiều nhóm khác nữa nhưng những nhóm ý chúng ta sẽ đi ở các bài viết khác nhé vì chúng khác là trừu tượng và dài.

    Hai nhóm chính của Recommendation Systems

    Các hệ thống gợi ý (RS) thường được chia làm 2 nhóm chính lớn:

    • Content-based systems: Hệ thống sẽ quan tâm đến đặc điểm của mục tiêu ví dụ như khi bạn nghe Lạc Trôi , Em của ngày hôm qua , Nắng ấm xa dần thì hệ thống sẽ phân tích ra các bài hát bạn vừa nghe là của Sơn Tùng thì hệ thống sẽ chuẩn đoán bạn đích thực là 1 SKY rồi thế là nhồi cho bạn 1 list nhạc của MTP luôn , User cũng tương tự trong bài toán gợi ý kết bạn hay lướt tinder ví dụ bạn hay thả tym cho các cô gái 1m5-1m6 tuổi 18-20 , nhà Tây Hồ đi Mer thì Hệ thống sẽ đề xuất cho bạn làm trạn vương
    • Collaborative filtering: Nhóm này thì đẳng cấp hơn khi gợi ý các item dựa trên sự tương quan (similarity) giữa các User và(hoặc) Item, hơi khó hiểu nhỉ để dễ hình dùng hơn là nhóm này gợi ý dựa trên rating của User với Item hay được recommended tới User dự trên những User có hành vi tương tự. Ví dụ: Có 4 cô A,B,C,D đều thích nhạc của Sơn Tùng và là SKY chân chính luôn. Ngoài ra hệ thống còn có thông tin 2 cô A,B đều ghét nhạc của JACK mà k biết 2 cô kia thế nào? Dựa trên phán đoán của hệ thống có thể đề xuất cả 2 cô còn lại đều ghét nhạc của JACK bằng cách rating 1 sao hoặc dislike nhạc và không bao giờ gợi ý nhạc JACK cho 2 cô còn lại . Trong CF này còn được chia ra làm 3 cách khách nhau như NCF (Neural Collaborative Filtering) , NBCF (Neighborhood-Based Collaborative Filtering) , MF(Matrix Factorization Collaborative Filtering) về chi tiết 3 cách này mình sẽ đi vào chi tiết ở các bài viết tới.

    Vậy chúng ta nên sử dụng phương pháp nào cho ứng dụng của mình?

    Có một điều dễ nhận thấy là nếu chúng ta sử dụng Collaborative filtering thì rõ ràng là chúng ta đã có 1 lượng data khá ổn định về rating , feedback của khách hàng để đưa ra đề xuất hợp lí và chính xác nhất chính vì vậy cách này được sử dụng khi bạn đã có 1 lượng thông tin đủ dày rồi nhưng cách này lại đưa ra đề xuất 1 cách chính xác hơn nhưng nếu bạn chưa có đủ lượng data cần thiết thì cách Content-based lại khá hữu ích khi bạn chưa có được lượng data về rating hay feedback mà chỉ có thông tin item mà User quan tâm. Chính vì vậy tùy vào tình huống , trường hợp của mỗi ứng dụng mà chúng ta sẽ sử dụng cách nào.

    Utility matrix

    Như đã đề cập, có hai thực thể chính trong các Recommendation Systems là users và items. Mỗi user sẽ có mức độ quan tâm (degree of preference) tới từng item khác nhau. Mức độ quan tâm này, nếu đã biết trước, được gán cho một giá trị ứng với mỗi cặp user-item. Giả sử rằng mức độ quan tâm được đo bằng giá trị user rate cho item, ta tạm gọi giá trị này là rating. Tập hợp tất cả các ratings, bao gồm cả những giá trị chưa biết cần được dự đoán, tạo nên một ma trận gọi là utility matrix. Xét vị dụ:

    Ví dụ về utility matrix

    Hình 1: Ví dụ về utility matrix với hệ thống Gợi ý.

    Các item được người dùng đánh giá từ 1 đến 5. Các dấu ‘?’ ứng với việc dữ liệu chưa tồn tại trong cơ sở dữ liệu. Nhiệm vụ của RS là phải tự điền các giá trị và các ô còn thiếu này và từ đó đưa ra gợi ý cho người dùng.

    Thông thường, có rất nhiều users và items trong hệ thống, và mỗi user thường chỉ rate một số lượng rất nhỏ các item, thậm chí có những user không rate item nào (với những users này thì cách tốt nhất là gợi ý các items phổ biến nhất). Vì vậy, lượng ô ‘?’ của utility matrix trong các bài toán đó thường là rất lớn, và lượng các ô đã được điền là một số rất nhỏ.

    Rõ ràng rằng càng nhiều ô được điền thì độ chính xác của hệ thống sẽ càng được cải thiện. Vì vậy, các hệ thống luôn luôn hỏi người dùng về sự quan tâm của họ tới sản phẩm, và muốn người dùng đánh giá càng nhiều sản phẩm càng tốt. Việc đánh giá các sản phẩm, vì thế, không những giúp các người dùng khác biết được chất lượng sản phẩm mà còn giúp hệ thống biết được sở thích của người dùng, qua đó có chính sách quảng cáo hợp lý.

    Không có utility matrix gần như là không thể đưa ra gợi ý chính xác tới người dùng cũng có thể đưa ra các sản phẩm phổ biến nhất. Vì vậy trong các Recommendation Systems, việc hoàn thiện hay xây dựng utility matrix là điều rất cần thiết nhưng việc xây dựng nó lại rất khó khăn vì khách hàng thường mua hàng xong là mất hút luôn mà k để lại bất kì dạng feedback gì cả chính vì vậy mà các ông lớn đã tìm ra rất nhiều cách để lấy cái feedback này của khách hàng và mình đúc kết lại có 3 cách chính sau để lấy rating của khách hàng:

    • Yêu cầu người dùng Rating sản phẩm, các ông lớn như Amazon, Alibaba , Shopee thường sẽ gửi mail liên tục nếu bạn không rating sẩn phẩm của họ hoặc thông báo đến bạn và rất nhiều trang thương mại điện tử cũng làm theo các cách này để lấy được rating của khách hàng nhưng nó lại khá là gây ức chế cho người dùng và dễ gây ra rating chống đối, không đúng mong muốn
    • Cách thứ hai đó là dựa theo hành vi người dùng , cách này khá là có triển vọng vì dựa vào hành vì chúng ta có thể kết luận được mức độ quan tâm của 1 user đến 1 item nào đó. Ví dụ bạn xem 1 video trên youtube rất nhiều lần thì có thể kết luận rằng bạn thích xem video đó hay là dựa vào các hành vi khách như Like , thả Tym trên facebook , mua 1 món đồ nào đó nhiều lần … Tất cả các hành vi của bạn đều phản ánh sự quan tâm của bạn đến 1 item nào đó vì trả ai ngáo đến nỗi xem đi xem lại 1 thứ nhiều lần trong khi rất ghét nó
    • Lấy rating từ 1 nguồn khác , điển hình là Facebook và Google , chắc chắn bạn đã thấy khi search google 1 cái gì đó hoặc tìm mua 1 món đồ nào đó trên các trang thương mại điện tử khác thì chỉ 1 lúc sau trên facebook bạn sẽ tràn ngập quảng cáo món đồ đó vì Facebook họ dùng 1 cái là Pixel. Anh em có thể hiểu nôm na là 1 đoạn mã nhỏ gắn vào cách website. Đoạn mã này hiểu nôm na nó cho Facebook biết ai là người đang sử dụng ứng dụng nào và cho Facebook biết các trạng thái của bạn như xem , tìm kiếm , thêm vào giỏ hàng , thanh toán … để đưa ra chính sách quảng cáo phù hợp . Kĩ thuật này còn gọi là Retargeting

    Có nhiều người thường cho rằng mấy ông lớn đang nghe lén bạn, nếu có thế thật thì chắc mấy ông lớn phải tìm hành tinh khác để lưu trữ dữ liệu và 1 thuật toán thần thánh nào đó để phân tích đống dữ liệu kia đủ nhanh

    Tản mạn đến đây thôi, ở bài viết sau mình sẽ đề cập chi tiết đến CBF, 1 trong cách nhánh chính của RS.Tấm chiếu mới trong viết bài về công nghệ như mình sẽ không tránh khỏi sai sót trong kiến thức, mong các huynh đệ có thể góp ý để có gạch xây nhà

    Tài liệu mình hay tham khảo

    1. Khá hay và nhiều kiến thức của các pháp sư US-UK AWS Machine Learning Blog

    2. Kho Dataset cho anh em GroupLens

    3. Mình cũng hay tham khảo ở đây TensorFlowBlog

    Còn rất nhiều Blog hay về AI , Machine Learning , Data Science nhưng khi nói về phần nào mình sẽ share về phần ý cho anh đỡ phân tâm nhé. Hẹn anh em ở bài viết lần sau nhé , mọi người có thể gọi mình là Yonko

  • Android Architecture – Tại sao chọn MVVM hơn là MVP

    Android Architecture – Tại sao chọn MVVM hơn là MVP

    Bài viết này, mình sẽ trình bày tại sao chọn MVVM hơn là MVP.

    I. Vấn đề

    Một số vấn đề để một lập trình viên quyết định chọn mô hình xây dựng ứng dụng như là làm sao để tái sử dụng code, dễ maintenance, dễ viết unit test hay dễ đọc hiểu với người mới vào trong dự án. Một số vấn đề trên dẫn đến việc chọn lựa mô hình khi bắt đầu một dự án mới là một điều hết sức quan trọng đối với mỗi lập trình viên. Hiện nay, có thể thấy 2 mô hình phổ biến nhất là MVVM và MVP. Trong bài viết này, chúng ta sẽ cùng tìm hiểu về chúng và xem cái nào ưu việt hơn.

    II. Giải pháp

    Bản thân Android được viết dưới dạng MVC trong đó Activity chịu trách nhiệm cho rất nhiều thứ trong đó bao gồm tất cả các logic. Với những ứng dụng đơn giản thì có thể mọi thứ vẫn còn dễ dàng, nhưng khi ứng dụng đủ lớn, số lượng logic tăng lên và mức độ vấn đề cũng tăng theo. Có nhiều mô hình tiếp cận khác nhau như MVP, MVVM,… được chứng minh là có thể giải quyết các vấn đề trên. Người ta có thể sử dụng bất kỳ cách tiếp cận nào, chúng thích ứng với các cách thay đổi một cách nhanh chóng,…

    III. Mục tiêu

    Xây dựng mọi thứ một cách phân tán như vậy để tách biệt dữ liệu – logic – view ra để đối với những project lớn khi số lượng logic và dữ liệu đủ lớn sẽ hữu ích trong việc mở rộng, bảo trì, test,…

    IV. Tại sao là MVVM?

    Có khá nhiều bài viết về MVP về sự sử dụng rộng rãi của mô hình này: Model — View — Presenter. Đó là một mô hình trưởng thành và ở mức độ nhất định, có thể giải quyết vấn đề nhưng vẫn có khá nhiều hạn chế và nó cần phải cải thiện một số thứ.

    Một mô hình MVP đơn giản như sau:

    Mô hình MVP

    Và một mô hình MVVM đơn giản như sau:

    Mô hình MVVM

    Hãy bắt đầu vào những hạn chế của MVP và cách chúng ta có thể khắc phục chúng bằng cách sử dụng MVVM.

    Đối với mỗi View thì đều yêu cầu 1 Presenter, đây là quy tắc ràng buộc cứng nhắc. Presenter giữ tham chiếu đến View và View cũng giữ tham chiếu đến Presenter. Mối quan hệ 1:1 và đó là vấn đề lớn nhất.

    Khi sự phức tạp hay độ lớn của ứng dụng tăng lên dẫn đến việc duy trì và xử lý mối quan hệ này cũng vậy.

    Chính vì những hạn chế trên của Presenter, MVVM được giới thiệ

    ViewModel là các class mô phỏng tương tác với logic/model layer và chỉ hiện trạng thái/ dữ liệu mà không quan tâm ai hoặc dữ liệu sẽ được tiêu thụ thế nào. Chỉ View giữ tham chiếu đến ViewModel và không có trường hợp ngược lại, điều này giải quyết vấn đề của Presenter và View. Một View có thể giữ tham chiếu nhiều ViewModel. Ngay cả một View cũng có thể giữ tham chiếu đến nhiều ViewModel.

    V. Khả năng test

    Bởi vì Presenter bị ràng buộc chặt chẽ với View, dẫn đến việc unit test trở nên hơi khó khăn. ViewModel thâm chí còn thân thiện hơn với Unit test dù chúng chỉ hiển thị trạng thái và do đó có thể được kiểm tra độc lập mà không yêu cầu kiểm tra dữ liệu được tiêu thụ như thế nào. Đây là 2 lý do chính làm cho sự phân biệt, lựa chọn rõ ràng ảnh hưởng đến khả năng unit test của 2 mô hình.

    VI. Tổng kết

    Các mô hình này đang tiếp tục phát triển và MVVM có thể nói tiềm năng để trở nên mạnh mẽ, hữu ích nhưng tuyệt vời để thực hiện. MVP cũng rất hữu dụng và phổ biến nhưng chưa có thể hoàn hảo. Không có thể chắc chắn về tương lai, phù hợp tốt cho tất cả các giải pháp. Người ta có thể thích hoặc không thích MVVM nhưng đó cũng không quá quan trọng, miễn là chúng ta đạt được mục tiêu đang đáp ứng tốt cho project phát triển. Đây cũng là một số cảm nhận khi mình sử dụng qua 2 mô hình này. Do chưa có kinh nghiệm nên bài viết này không thể tránh khỏi sai sót, rất mong nhận được góp ý từ mọi người.

    Trên đây là 1 số chia sẻ về hạn chế của MVP và nó có thể khắc phục bằng MVVM. Cảm ơn các bạn đã theo dõi bài viết!

    Tham khảo: https://android.jlelse.eu/why-to-choose-mvvm-over-mvp-android-architecture-33c0f2de5516

  • Tổng quan thư viện Underthesea – bộ công cụ mã nguồn mở xử lý ngôn ngữ tự nhiên tiếng Việt

    Tổng quan thư viện Underthesea – bộ công cụ mã nguồn mở xử lý ngôn ngữ tự nhiên tiếng Việt

    Giới thiệu thư viện Underthesea – bộ công cụ mã nguồn mở xử lý ngôn ngữ tự nhiên tiếng Việt

    Mở đầu

    Underthesea là một toolkit hỗ trợ cho việc nghiên cứu và phát triển xử lý ngôn ngữ tự nhiên tiếng Việt. Underthesea ra đời vào tháng 3 năm 2017, trong bối cảnh ở Việt Nam đã có một số toolkit khá tốt như vn.vitk, pyvi, nhưng vẫn thiếu một toolkit hoàn chỉnh, mã nguồn mở, dễ dàng cài đặt và sử dụng như các sản phẩm tương đương đối với tiếng Anh như nltk, polyglot, spacy.

    Trong bài viết này chúng ta sẽ tìm hiểu sơ qua về Underthesea và một số cách sử dụng của nó.

    Underthesea là :

    1. Một bộ công cụ NLP tiếng Việt <br> Underthesea là một mã nguồn mở bằng Python bao gồm các bộ dữ liệu (data sets) và các hướng dẫn hỗ trợ nghiên cứu và phát triển trong xử lý ngôn ngữ tự nhiên tiếng Việt (Vietnamese Natural Language Processing). Nó cung cấp các API cực kỳ dễ dàng để áp dụng các mô hình pretrained NLP cho văn bản tiếng Việt, chẳng hạn như phân đoạn từ, gắn thẻ một phần giọng nói(PoS), nhận dạng thực thể có tên (NER), phân loại văn bản và phân tích cú pháp phụ thuộc.

    2. Một thư viện Pytorch <br> Underthesea được hỗ trợ bởi một trong những thư viên học sâu phổ biến nhất, Pytorch, giúp nó dễ dàng train các mô hình học sâu và thử nghiệp các phương pháp tiếp cận mới bằng cách sử dụng các Module và Class của Underthesea

    3. Một phần mềm mã nguồn mở <br> Underthesea được công bố theo giấy phép GNU General Public License v3.0. Các quyền của giấy phép này có điều kiện là cung cấp mã nguồn hoàn chỉnh của các tác phẩm được cấp phép và sửa đổi, bao gồm các tác phẩm lớn hơn sử dụng tác phẩm được cấp phép, theo cùng một giấy phép.

    Cài đặt

    Để cài đặt underthesea :

    $ pip install underthesea
    

    Hướng dẫn

    1. Phân đoạn câu (Sentence Segmentation)
    >>> from underthesea import sent_tokenize
    >>> text = 'Taylor cho biết lúc đầu cô cảm thấy ngại với cô bạn thân Amanda nhưng rồi mọi thứ trôi qua nhanh chóng. Amanda cũng thoải mái với mối quan hệ này.'
    
    >>> sent_tokenize(text)
    [
      "Taylor cho biết lúc đầu cô cảm thấy ngại với cô bạn thân Amanda nhưng rồi mọi thứ trôi qua nhanh chóng.",
      "Amanda cũng thoải mái với mối quan hệ này."
    ]
    
    1. Phân đoạn từ (Word Segmentation)
    >>> from underthesea import word_tokenize
    >>> sentence = 'Chàng trai 9X Quảng Trị khởi nghiệp từ nấm sò'
    
    >>> word_tokenize(sentence)
    ['Chàng trai', '9X', 'Quảng Trị', 'khởi nghiệp', 'từ', 'nấm', 'sò']
    
    >>> word_tokenize(sentence, format="text")
    'Chàng_trai 9X Quảng_Trị khởi_nghiệp từ nấm sò'
    
    1. Gán nhãn POS
    >>> from underthesea import pos_tag
    >>> pos_tag('Chợ thịt chó nổi tiếng ở Sài Gòn bị truy quét')
    [('Chợ', 'N'),
     ('thịt', 'N'),
     ('chó', 'N'),
     ('nổi tiếng', 'A'),
     ('ở', 'E'),
     ('Sài Gòn', 'Np'),
     ('bị', 'V'),
     ('truy quét', 'V')]
    
    1. Chunking
    >>> text = 'Bác sĩ bây giờ có thể thản nhiên báo tin bệnh nhân bị ung thư?'
    >>> chunk(text)
    [('Bác sĩ', 'N', 'B-NP'),
     ('bây giờ', 'P', 'I-NP'),
     ('có thể', 'R', 'B-VP'),
     ('thản nhiên', 'V', 'I-VP'),
     ('báo tin', 'N', 'B-NP'),
     ('bệnh nhân', 'N', 'I-NP'),
     ('bị', 'V', 'B-VP'),
     ('ung thư', 'N', 'I-VP'),
     ('?', 'CH', 'O')]
    
    1. Phân tích cú pháp phụ thuộc
    >>> from underthesea import dependency_parse
    >>> text = 'Tối 29/11, Việt Nam thêm 2 ca mắc Covid-19'
    >>> dependency_parse(text)
    [('Tối', 5, 'obl:tmod'),
     ('29/11', 1, 'flat:date'),
     (',', 1, 'punct'),
     ('Việt Nam', 5, 'nsubj'),
     ('thêm', 0, 'root'),
     ('2', 7, 'nummod'),
     ('ca', 5, 'obj'),
     ('mắc', 7, 'nmod'),
     ('Covid-19', 8, 'nummod')]
    
    1. Gán nhãn thực thể có tên (Named Entity Recognition)
    >>> from underthesea import ner
    >>> text = 'Chưa tiết lộ lịch trình tới Việt Nam của Tổng thống Mỹ Donald Trump'
    >>> ner(text)
    [('Chưa', 'R', 'O', 'O'),
     ('tiết lộ', 'V', 'B-VP', 'O'),
     ('lịch trình', 'V', 'B-VP', 'O'),
     ('tới', 'E', 'B-PP', 'O'),
     ('Việt Nam', 'Np', 'B-NP', 'B-LOC'),
     ('của', 'E', 'B-PP', 'O'),
     ('Tổng thống', 'N', 'B-NP', 'O'),
     ('Mỹ', 'Np', 'B-NP', 'B-LOC'),
     ('Donald', 'Np', 'B-NP', 'B-PER'),
     ('Trump', 'Np', 'B-NP', 'I-PER')]
    
    1. Phân loại văn bản
    >>> from underthesea import classify
    
    >>> classify('HLV đầu tiên ở Premier League bị sa thải sau 4 vòng đấu')
    ['The thao']
    
    >>> classify('Hội đồng tư vấn kinh doanh Asean vinh danh giải thưởng quốc tế')
    ['Kinh doanh']
    
    >> classify('Lãi suất từ BIDV rất ưu đãi', domain='bank')
    ['INTEREST_RATE']
    
    1. Phân tích cảm xúc
    >>> from underthesea import sentiment
    
    >>> sentiment('hàng kém chất lg,chăn đắp lên dính lông lá khắp người. thất vọng')
    negative
    >>> sentiment('Sản phẩm hơi nhỏ so với tưởng tượng nhưng chất lượng tốt, đóng gói cẩn thận.')
    positive
    
    >>> sentiment('Đky qua đường link ở bài viết này từ thứ 6 mà giờ chưa thấy ai lhe hết', domain='bank')
    ['CUSTOMER_SUPPORT#negative']
    >>> sentiment('Xem lại vẫn thấy xúc động và tự hào về BIDV của mình', domain='bank')
    ['TRADEMARK#positive']
    
    1. Tài nguyên NLP tiếng Việt

    Danh sách tài nguyên

    $ underthesea list-data
    | Name                | Type        | License | Year | Directory                    |
    |---------------------+-------------+---------+------+------------------------------|
    | UIT_ABSA_RESTAURANT | Sentiment   | Open    | 2021 | datasets/UIT_ABSA_RESTAURANT |
    | UIT_ABSA_HOTEL      | Sentiment   | Open    | 2021 | datasets/UIT_ABSA_HOTEL      |
    | SE_Vietnamese-UBS   | Sentiment   | Open    | 2020 | datasets/SE_Vietnamese-UBS   |
    | CP_Vietnamese-UNC   | Plaintext   | Open    | 2020 | datasets/CP_Vietnamese-UNC   |
    | DI_Vietnamese-UVD   | Dictionary  | Open    | 2020 | datasets/DI_Vietnamese-UVD   |
    | UTS2017-BANK        | Categorized | Open    | 2017 | datasets/UTS2017-BANK        |
    | VNTQ_SMALL          | Plaintext   | Open    | 2012 | datasets/LTA                 |
    | VNTQ_BIG            | Plaintext   | Open    | 2012 | datasets/LTA                 |
    | VNESES              | Plaintext   | Open    | 2012 | datasets/LTA                 |
    | VNTC                | Categorized | Open    | 2007 | datasets/VNTC                |
    
    $ underthesea list-data --all
    

    Download tài nguyên

    $ underthesea download-data VNTC
    100%|██████████| 74846806/74846806 [00:09<00:00, 8243779.16B/s]
    Resource VNTC is downloaded in ~/.underthesea/datasets/VNTC folder
    

    Các tính năng sắp ra mắt

    • Dịch máy
    • Chuyển văn bản thành giọng nói
    • Nhận dạng giọng nói tự động

    Kết bài

    Với Underthesea, chúng ta có thể dễ dàng cài đặt, sử dụng và tiết kiệm được lượng lớn thời gian thay vì phải gán nhãn bằng tay. Underthesea cũng là thư viện đắc lực hỗ trợ xử lý dữ liệu đầu vào cho rất nhiều bài toán khác.

    Cảm ơn các bạn đã giành thời gian đọc.

    Tham khảo: https://pypi.org/project/underthesea/

  • Tổng quan về Underthesea

    # **Giới thiệu thư viện Underthesea – bộ công cụ mã nguồn mở xử lý ngôn ngữ tự nhiên tiếng Việt**
    ## **Mở đầu**
    Underthesea là một toolkit hỗ trợ cho việc nghiên cứu và phát triển xử lý ngôn ngữ tự nhiên tiếng Việt. Underthesea ra đời vào tháng 3 năm 2017, trong bối cảnh ở Việt Nam đã có một số toolkit khá tốt như vn.vitk, pyvi, nhưng vẫn thiếu một toolkit hoàn chỉnh, mã nguồn mở, dễ dàng cài đặt và sử dụng như các sản phẩm tương đương đối với tiếng Anh như nltk, polyglot, spacy.
    Trong bài viết này chúng ta sẽ tìm hiểu sơ qua về Underthesea và một số cách sử dụng của nó.## **Underthesea** là : 1.  **Một bộ công cụ NLP tiếng Việt** <br>Underthesea là một mã nguồn mở bằng Python bao gồm các bộ dữ liệu (data sets) và các hướng dẫn hỗ trợ nghiên cứu và phát triển trong xử lý ngôn ngữ tự nhiên tiếng Việt ([Vietnamese Natural Language Processing](https://github.com/undertheseanlp/underthesea)). Nó cung cấp các API cực kỳ dễ dàng để áp dụng các mô hình pretrained NLP cho văn bản tiếng Việt, chẳng hạn như phân đoạn từ, gắn thẻ một phần giọng nói(PoS), nhận dạng thực thể có tên (NER), phân loại văn bản và phân tích cú pháp phụ thuộc.
    2. **Một thư viện Pytorch** <br>Underthesea được hỗ trợ bởi một trong những thư viên học sâu phổ biến nhất, [Pytorch](https://pytorch.org/), giúp nó dễ dàng train các mô hình học sâu và thử nghiệp các phương pháp tiếp cận mới bằng cách sử dụng các Module và Class của Underthesea
    3. **Một phần mềm mã nguồn mở** <br>Underthesea được công bố theo giấy phép GNU General Public License v3.0. Các quyền của giấy phép này có điều kiện là cung cấp mã nguồn hoàn chỉnh của các tác phẩm được cấp phép và sửa đổi, bao gồm các tác phẩm lớn hơn sử dụng tác phẩm được cấp phép, theo cùng một giấy phép.
    ### **Cài đặt**
    Để cài đặt underthesea :“`$ pip install underthesea“`
    ### **Hướng dẫn**
    1. **Phân đoạn câu** (Sentence Segmentation)“`>>> from underthesea import sent_tokenize>>> text = ‘Taylor cho biết lúc đầu cô cảm thấy ngại với cô bạn thân Amanda nhưng rồi mọi thứ trôi qua nhanh chóng. Amanda cũng thoải mái với mối quan hệ này.’
    >>> sent_tokenize(text)[  “Taylor cho biết lúc đầu cô cảm thấy ngại với cô bạn thân Amanda nhưng rồi mọi thứ trôi qua nhanh chóng.”,  “Amanda cũng thoải mái với mối quan hệ này.”]“`
    2. **Phân đoạn từ** (Word Segmentation)“`>>> from underthesea import word_tokenize>>> sentence = ‘Chàng trai 9X Quảng Trị khởi nghiệp từ nấm sò’
    >>> word_tokenize(sentence)[‘Chàng trai’, ‘9X’, ‘Quảng Trị’, ‘khởi nghiệp’, ‘từ’, ‘nấm’, ‘sò’]
    >>> word_tokenize(sentence, format=”text”)’Chàng_trai 9X Quảng_Trị khởi_nghiệp từ nấm sò’“`
    3. **Gán nhãn POS**“`>>> from underthesea import pos_tag>>> pos_tag(‘Chợ thịt chó nổi tiếng ở Sài Gòn bị truy quét’)[(‘Chợ’, ‘N’), (‘thịt’, ‘N’), (‘chó’, ‘N’), (‘nổi tiếng’, ‘A’), (‘ở’, ‘E’), (‘Sài Gòn’, ‘Np’), (‘bị’, ‘V’), (‘truy quét’, ‘V’)]“`
    4. **Chunking**“`>>> from underthesea import chunk>>> text = ‘Bác sĩ bây giờ có thể thản nhiên báo tin bệnh nhân bị ung thư?’>>> chunk(text)[(‘Bác sĩ’, ‘N’, ‘B-NP’), (‘bây giờ’, ‘P’, ‘I-NP’), (‘có thể’, ‘R’, ‘B-VP’), (‘thản nhiên’, ‘V’, ‘I-VP’), (‘báo tin’, ‘N’, ‘B-NP’), (‘bệnh nhân’, ‘N’, ‘I-NP’), (‘bị’, ‘V’, ‘B-VP’), (‘ung thư’, ‘N’, ‘I-VP’), (‘?’, ‘CH’, ‘O’)]“`
    5. **Phân tích cú pháp phụ thuộc**“`>>> from underthesea import dependency_parse>>> text = ‘Tối 29/11, Việt Nam thêm 2 ca mắc Covid-19’>>> dependency_parse(text)[(‘Tối’, 5, ‘obl:tmod’), (’29/11′, 1, ‘flat:date’), (‘,’, 1, ‘punct’), (‘Việt Nam’, 5, ‘nsubj’), (‘thêm’, 0, ‘root’), (‘2’, 7, ‘nummod’), (‘ca’, 5, ‘obj’), (‘mắc’, 7, ‘nmod’), (‘Covid-19’, 8, ‘nummod’)]“`
    6. **Gán nhãn thực thể có tên** (Named Entity Recognition)“`>>> from underthesea import ner>>> text = ‘Chưa tiết lộ lịch trình tới Việt Nam của Tổng thống Mỹ Donald Trump’>>> ner(text)[(‘Chưa’, ‘R’, ‘O’, ‘O’), (‘tiết lộ’, ‘V’, ‘B-VP’, ‘O’), (‘lịch trình’, ‘V’, ‘B-VP’, ‘O’), (‘tới’, ‘E’, ‘B-PP’, ‘O’), (‘Việt Nam’, ‘Np’, ‘B-NP’, ‘B-LOC’), (‘của’, ‘E’, ‘B-PP’, ‘O’), (‘Tổng thống’, ‘N’, ‘B-NP’, ‘O’), (‘Mỹ’, ‘Np’, ‘B-NP’, ‘B-LOC’), (‘Donald’, ‘Np’, ‘B-NP’, ‘B-PER’), (‘Trump’, ‘Np’, ‘B-NP’, ‘I-PER’)]“`
    7. **Phân loại văn bản**“`>>> from underthesea import classify
    >>> classify(‘HLV đầu tiên ở Premier League bị sa thải sau 4 vòng đấu’)[‘The thao’]
    >>> classify(‘Hội đồng tư vấn kinh doanh Asean vinh danh giải thưởng quốc tế’)[‘Kinh doanh’]
    >> classify(‘Lãi suất từ BIDV rất ưu đãi’, domain=’bank’)[‘INTEREST_RATE’]“`
    8. **Phân tích cảm xúc**“`>>> from underthesea import sentiment
    >>> sentiment(‘hàng kém chất lg,chăn đắp lên dính lông lá khắp người. thất vọng’)negative>>> sentiment(‘Sản phẩm hơi nhỏ so với tưởng tượng nhưng chất lượng tốt, đóng gói cẩn thận.’)positive
    >>> sentiment(‘Đky qua đường link ở bài viết này từ thứ 6 mà giờ chưa thấy ai lhe hết’, domain=’bank’)[‘CUSTOMER_SUPPORT#negative’]>>> sentiment(‘Xem lại vẫn thấy xúc động và tự hào về BIDV của mình’, domain=’bank’)[‘TRADEMARK#positive’]“`
    9. Tài nguyên NLP tiếng Việt
    Danh sách tài nguyên
    “`$ underthesea list-data| Name                | Type        | License | Year | Directory                    ||———————+————-+———+——+——————————|| UIT_ABSA_RESTAURANT | Sentiment   | Open    | 2021 | datasets/UIT_ABSA_RESTAURANT || UIT_ABSA_HOTEL      | Sentiment   | Open    | 2021 | datasets/UIT_ABSA_HOTEL      || SE_Vietnamese-UBS   | Sentiment   | Open    | 2020 | datasets/SE_Vietnamese-UBS   || CP_Vietnamese-UNC   | Plaintext   | Open    | 2020 | datasets/CP_Vietnamese-UNC   || DI_Vietnamese-UVD   | Dictionary  | Open    | 2020 | datasets/DI_Vietnamese-UVD   || UTS2017-BANK        | Categorized | Open    | 2017 | datasets/UTS2017-BANK        || VNTQ_SMALL          | Plaintext   | Open    | 2012 | datasets/LTA                 || VNTQ_BIG            | Plaintext   | Open    | 2012 | datasets/LTA                 || VNESES              | Plaintext   | Open    | 2012 | datasets/LTA                 || VNTC                | Categorized | Open    | 2007 | datasets/VNTC                |
    $ underthesea list-data –all“`
    Download tài nguyên
    “`$ underthesea download-data VNTC100%|██████████| 74846806/74846806 [00:09<00:00, 8243779.16B/s]Resource VNTC is downloaded in ~/.underthesea/datasets/VNTC folder“`
    ### Các tính năng sắp ra mắt– Dịch máy- Chuyển văn bản thành giọng nói- Nhận dạng giọng nói tự động
    ### **Kết bài**
    Với Underthesea, chúng ta có thể dễ dàng cài đặt, sử dụng và tiết kiệm được lượng lớn thời gian thay vì phải gán nhãn bằng tay. Underthesea cũng là thư viện đắc lực hỗ trợ xử lý dữ liệu đầu vào cho rất nhiều bài toán khác. <br>Cảm ơn các bạn đã giành thời gian đọc. <br>Tham khảo: [https://pypi.org/project/underthesea/](https://pypi.org/project/underthesea/)

  • Clean Architecture trong Android

    Clean Architecture trong Android

    Giới thiệu

    Clean Architecture là một kiến trúc lập trình được Robert C. Martin đề cập tại blog của ông vào năm 2012, đó là sự kiện quan trọng trong tư tưởng thiết kế phần mềm ảnh hưởng cho đến bây giờ.

    Clean Architecture có thể được sử dụng trong bất kỳ ứng dụng và nền tảng nào. Bài viết này mình xin được tập trung vào kiến trúc sạch trong phát triển ứng dụng android.

    Tổng quan kiến trúc sạch

    Có rất nhiều kiến trúc khác nhau trong lập trình nhưng chúng đều có một mục tiêu chung là tách biệt các mối quan tâm. Để đạt được sự tách biệt này cách chia phần mềm thành các layer đảm nhận các nghiệp vụ khác nhau.

    Một vài đặc điểm:

    • Dễ dàng test, các bussiness logic có thể test mà không có giao diện người dùng, cơ sở dữ liệu, bất kỳ yếu tố bên ngoài nào khác.
    • Giao diện người dùng có thể dễ dàng thay đổi mà không làm thay đổi phần còn lại của hệ thống.
    • Độc lập với các dữ liệu, ta có thể chuyển đổi các loại dữ liệu khác nhau mà không ảnh hưởng đến các layer khác.
    • Độc lập với các framework, điều này cho phép sử dụng các framework như các công cụ, thay vì phải nhồi nhét hệ thống của bạn phụ thuộc vào chúng.
    • Một layer chỉ biết layer phía dưới nó, layer trong cùng sẽ không biết gì về các layer khác(isolated) => tách biệt giữa các layer, layer business logic bên trong sẽ không phụ thuộc vào các layer khác.
      Mộ- Các vòng tròn đồng tâm càng ở trong càng cấp cao, module cấp cao không phụ thuộc vào module cấp thấp(Dependency Inversion principle). Ta sẽ rõ hơn trong chi tiết từng layer khi xây dựng ứng dụng android.
    • Áp dụng nguyên tắc SOLID.

    Clean Architecture trong ứng dụng Android

    Tùy thuộc vào đặc thù, quy mô dự án mà ta phân chia thành các layer khác nhau. Trong dự án android ta thường chia thành ba layer chính:

    • Domain
    • Data
    • Presenter

    Mỗi layer là một module trong Android Studio, giờ ta sẽ đi vào chi tiết từng layer.

    Domain layer

    • Domain layer là lớp chứa tất cả model và toàn bộ bussiness logic của ứng dụng, có thể coi đây là nơi chứa các policy còn các layer khác là nơi chứa các cơ chế.
    • Domain layer nằm trong cùng do đó sẽ không biết bất kỳ layer nào khác bên ngoài.
      Đây là module cấp cao, không phụ thuộc vào bất kỳ implementation của module cấp thấp nào mà chỉ phụ thuộc thông qua abstraction
    • Mỗi usecase đảm nhiệm một nhiệm vụ duy nhất (Single responsibility principle), ví dụ: GetData(), AddData(), …

    Tạo module domain với lib java or kotlin, do domain layer không phụ thuộc vào bất kỳ android framwork nào. Do domain layer chỉ chứa bussiness logic của app.


    Ta có thể thấy file gradle của module domain không chứa dependencies của android framework.

    Ở domain layer chỉ định nghĩa interface Repository, data layer chứa implementation mà domain đã định nghĩa.

    Presenter layer phụ thuộc usecase.

    Usecase tương tự như trong UML chứa một chức năng duy nhất, usecase chỉ phụ thuộc vào interface SchoolRepository (Nguyên tắc DI trong SOLID) mà không phụ thuộc trực tiếp repository tại data layer.

    class GetAllSchoolUseCase @Inject constructor(
        private val schoolRepository: SchoolRepository
    ) : IUseCase<Flow<List<School>>> {
    
        override fun invoke(): Flow<List<School>> = schoolRepository.getAllSchool()
    }

    Data layer

    Lớp này cung cấp cách thức để truy cập các nguồn dữ liệu trong room database hoặc internet. Các triển khai này sử dụng Repository pattern.

    Data layer được tạo là module library android.

    Tại data layer sẽ tạo class triển khai interface repository mà định nghĩa trong domain layer.

    class SchoolRepositoryImpl @Inject constructor(
        @DispatcherIO private val dispatcher: CoroutineDispatcher,
        private val schoolDao: SchoolDao,
        private val studentDao: StudentDao
    ) : SchoolRepository {
        ...
    }

    Để chuyển đổi qua lại giữa model và entity giữa domain layer và data layer ta dùng các function mapper.

    fun SchoolWithStudents.toSchool(): School {
        return this.run {
            School(
                schoolEntity.schoolID,
                schoolEntity.schoolName,
                schoolEntity.address,
                students.toStudents()
            )
        }
    }
    
    fun School.fromSchool(): SchoolEntity {
        return this.run {
            SchoolEntity(schoolID, schoolName, address)
        }
    }
    

    Presenter layer

    Tại đây chứa các code liên quan đến giao diện người dùng, layer này sẽ dependent các module khác.

    Do sử dụng một vài dependencies tại module data nên mình sử dụng api project(":data").

    Sử dụng MVVM

    • View: chịu trách nhiệm vẽ UI người dùng (Activity, Fragment, …). Lắng nghe hành động từ người dùng và gọi ViewModel sử lý, observe LiveData trong ViewModel.
    • Model: Chứa các bussiness login và data
    • ViewModel: Cầu nối giữa data và UI, ViewModel sẽ sử dụng các UseCase tại domain layer để thực hiện các nhiệm vụ.

    Mỗi màn hình(feature) sẽ là một subpackage trong package ui.

    ViewModel sử dụng các usecase để thực hiện các task khác nhau.

    @HiltViewModel
    class SchoolListViewModel @Inject constructor(
        private val getAllSchoolUseCase: GetAllSchoolUseCase,
        private val deleteSchoolUseCase: DeleteSchoolUseCase,
        private val deleteStudentUseCase: DeleteStudentUseCase,
        private val updateSchoolUseCase: UpdateSchoolUseCase,
        private val updateStudentUseCase: UpdateStudentUseCase
    ) : BaseViewModel<SchoolsViewState>() {
         ...
    }

    Kết luận

    Clean Architecture rất phù hợp với những dự án lớn phức tạp, có nhiều bussiness logic, giúp cho việc phát triển các tính năng và maintain dễ dàng hơn. Tuy nhiên với những dự án nhỏ và đơn giản hơn thì việc sử dụng clean architecture không mang lại hiệu quả và mất thời gian cho việc xây dựng ban đầu.

    Do chưa có kinh nghiệm nên bài viết này không thể tránh khỏi sai sót, rất mong nhận được góp ý từ các Anh.
    Author: CuongNV83

  • Lottie Animations in an Android App

    Lottie Animations in an Android App

    Lottie is an open source animation file format that’s tiny, high quality, interactive, and can be manipulated at runtime. The top 500 apps on the App store now use Lottie to engage users and enhance conversions.A Lottie is an open-source text and vector-based file format that is easy to ship. Its cross-platform capabilities, tiny file size, and scriptable and interactive nature make it popular with designers and developers. Motion evokes emotion. It humanizes your app or website, adds empathetic experiences that entertain and engage. Lottie is the easiest way to bring motion to your apps and platforms.

    Getting Started

    Choose your Lottie

    Choose the Lottie for your Android app. You may have your own or if you don’t , you can select one from Lottie Files if you can’t find a lottie that matches your Android App , you can create your own with Adobe After Effects but this is actually quite difficult

    It’s important to test your selected Lottie using the LottieFiles app for Android to make sure the animation you’ve chosen will play the same Android as sometimes not all animations are built with features supported in the Lottie Android player.

    Just scan the QR code under the animation on LottieFiles with the app to preview the animation.

    Setup your project

    Getting started with Lottie is very straightforward. This guide assumes that you are using Android Studio as your IDE. If you’re using a different IDE, you can still use the same instructions.

    Download

    dependencies {
        def lottieVersion = "3.4.0" 
        implementation 'com.airbnb.android:lottie:$lottieVersion'
    }
    

    Bundling animations with your app

    If you need your animations to work offline, you can bundle your animations with your application by including them in your projects raw resources.

    If your project does not have one, create it by going to File>New>Folder>Raw Resources Folder. If your animation contains images, you can bundle them all together in a .zip with your .json and follow the same procedure.

    Download the animation, rename it to animation.json or animation.zip depending on your use-case, and place into your raw resources folder.

    Add LottieAnimationView to your layout

         <com.airbnb.lottie.LottieAnimationView
                    android:layout_marginEnd="20dp"
                    app:lottie_autoPlay="true"
                    app:lottie_loop="true"
                    app:lottie_rawRes="@raw/lottieAnimation"
                    android:layout_alignParentRight="true"
                    android:layout_centerVertical="true"
                    android:layout_width="30dp"
                    android:layout_height="30dp"/>
    

    Your first Android project with Lottie animations is ready to go!. Now you can build your Android App and you can see your app look pretty good or better than use normal view. You can learn more in The official documentation for lottie-android

    That’s how you get started but there’s so much more you can do. For more tips on how to use Lottie with your projects I will mention in next Post

    Authors

    [email protected]

  • TaskGroup Swift Part 2

    TaskGroup Swift Part 2

    Nếu đã đọc bài viết trước của mình về task group, mọi người sẽ hiểu bản chất task group, cách khởi tạo, cách add 1 task con, và cách nhận result của tất cả các task con đó.

    Tuy nhiên, trong bài viết đó mình không đi sâu vào 1 phần quan trọng của task group, đó là việc handle error.

    Nay mình rảnh rảnh nên lên gõ 1 bài về chủ đề này ?

       

    Đầu mục bài viết

       

    1-Tổng quát

    Như chúng ta đã biết, TaskGroup là 1 tập hợp những task con thực thi đồng thời.

    Tuy nhiên, sẽ có vấn đề được đặt ra là khi có bất kỳ 1 trong những task con gặp lỗi, task group handle việc đó như nào? Điều gì sẽ xảy ra cho những task con khác đang thực thi?

    Ở bài viết này, mình sẽ đưa ra 2 cách cơ bản để handle error trong một task group, đó là:

    – Throw error

    – Trả về result của tất cả các task con

    Ta sẽ đi sâu vào từng cách ở những phần bên dưới.

       

    2-Throw error

    Cách đầu tiên là throw error thông qua withThrowingTaskGroup.

    Nhưng trước tiên, chúng ta cần phải hiểu một task group sẽ hoạt động thế nào khi một task con của nó gặp lỗi:

    1. Một task group sẽ chỉ throw ra error đầu tiên mà task con của nó gặp phải, task group sau đó sẽ bỏ qua tất cả những error sau. Tức là khi bất kỳ task con nào trong quá trình thực thi mà gặp phải lỗi, task group sẽ throw lỗi đó, và sẽ không quan tâm đến bất kỳ error nào task con của nó gặp phải sau đó nữa.

    2. Khi một task con throw 1 error, tất cả các task con còn lại (kể cả những task đang thực thi) sẽ được đánh dấu.

    3. Những task con còn lại mà đã được đánh dấu đó sẽ tiếp tục thực thi cho đến khi chúng ta can thiệp và dừng chúng lại.

    4. Khi chúng ta đã can thiệp và dừng việc thực thi của những task con còn lại đó, những task con đó sẽ không trigger việc add vào result tổng của 1 task group nữa (kể cả với những task con đã hoàn thành việc thực thi).

    Để hiểu rõ hơn, mình có tạo ra 1 struct DemoChildTask, mà trong đó xử lý việc chia 2 số cho nhau, và sẽ throw ra error khi có bất kỳ phép chia nào cho 0

    enum ErrorCase: Error {
        case divideToZero
    }
    
    struct DemoChildTask {
        let name: String
        let a: Double
        let b: Double
        let sleepDuration: UInt64
        
        func execute() async throws -> Double {
            // Sleep for x seconds
            await Task.sleep(sleepDuration * 1_000_000_000)
            
            // Throw error when divisor is zero
            guard b != 0 else {
                print("\(name) throw error")
                throw ErrorCase.divideToZero
            }
            
            let result = a/b
            print("\(name) has been completed: \(result)")
            return result
        }
    }
    

    Tiếp theo, mình tạo ra 1 task group và add các task con vào trong đó. Các task con đó sẽ thực thi DivideOperation và trả ra 2 giá trị name và result.

    Khi tất cả các task con hoàn thành, task group sẽ add các result con đó vào một dictionary result tổng mà trong đó chứa tất cả giá trị name và result của task con.

    Bất kể khi nào một trong những task con gặp error, nó sẽ throw và truyền ra cho task group, và task group sẽ throw error đó.

    let demoOperation: [DemoChildTask] = [DemoChildTask(name: "operation-0", a: 4, b: 1, sleepDuration: 3),
                                            DemoChildTask(name: "operation-1", a: 5, b: 2, sleepDuration: 1),
                                            DemoChildTask(name: "operation-2", a: 10, b: 5, sleepDuration: 2),
                                            DemoChildTask(name: "operation-3", a: 5, b: 0, sleepDuration: 2),
                                            DemoChildTask(name: "operation-4", a: 4, b: 2, sleepDuration: 5)]
    
    Task {
        do {
            let allResult = try await withThrowingTaskGroup(of: (String, Double).self,
                                                            returning: [String: Double].self,
                                                            body: { taskGroup in
                // Loop through operations array
                for operation in demoOperation {
                    
                    // Add child task to task group
                    taskGroup.addTask {
                        
                        // Execute slow operation
                        let value = try await operation.execute()
                        
                        // Return child task result
                        return (operation.name, value)
                    }
                }
                
                // Collect results of all child task in a dictionary
                var childTaskResults = [String: Double]()
                
                for try await result in taskGroup {
                    // Set operation name as key and operation result as value
                    childTaskResults[result.0] = result.1
                }
                // All child tasks finish running, thus return task group result
                return childTaskResults
            })
            
            print("Task group completed: \(allResult)")
        } catch {
            print("Task group error: \(error)")
        }
    }
    

    withThrowingTaskGroup về cơ bản giống với withTaskGroup, tuy nhiên chúng ta cần sử dụng keyword try để có thể throw error. Đồng thời cũng cần dùng try khi call func execute() của task con, điều này cho phép error throw từ execute() có thể truyền ra cho task group. Bên cạnh đó, try cũng phải được dùng khi tổng hợp result từ các task con.

    Khi chạy đoạn code trên, ta sẽ nhận được đoạn log như này:

    Đoạn output trên cho chúng ta thấy “operation-3” sẽ throw ra lỗi vì đã chia cho 0.

    Mặc dù đoạn code trên đã khá hoàn chỉnh cho việc handle error với throw, tuy nhiên để tối ưu hơn, ta sẽ không muốn các task con khác thực thi tiếp khi một task con đã throw ra error. Đây là lúc chúng ta cần phải can thiệp vào việc thực thi của chúng như mình đã note bên trên.

    Để làm được việc này, đơn giản có thể sử dụng Task.checkCancellation(). Func này dùng để check những task nào còn đang thực thi.

    func execute() async throws -> Double {
            // Sleep for x seconds
            await Task.sleep(sleepDuration * 1_000_000_000)
            
            // Check for cancellation.
            try Task.checkCancellation()
            
            // Throw error when divisor is zero
    //        guard b != 0 else {
    //            print("\(name) throw error")
    //            throw ErrorCase.divideToZero
    //        }
    //
    //        let result = a/b
    //        print("\(name) has been completed: \(result )")
    //        return result
        }
    

    Về cơ bản, khi func checkCancellation() gặp bất kỳ task con nào đã được đánh dấu, nó sẽ dừng task con đó và throw ra CancellationError . Tuy nhiên, như mình đã đề cập, việc throw này không mang quá nhiều ý nghĩa vì task group sẽ reject toàn bộ những error này.

    Khi chạy lại đoạn code trên với checkCancellation(), ta sẽ nhận được log như này:

       

    3-Trả về result

    Cách thứ 2 này thực ra hoàn toàn ngược lại với cách đầu tiên. Cách này cơ bản task group sẽ ignore toàn bộ task con mà throw error, mà sẽ chỉ trả về tất cả result của task con thực thi thành công.

    Cách 2 này code khá giống với cách 1, chỉ khác là bây giờ sẽ sử dụng non-throwing task group thay vì throw task group, và sẽ ignore toàn bộ error với try?:

    Task {
        let allResult = await withTaskGroup(of: (String, Double)?.self,
                                            returning: [String: Double].self,
                                            body: { taskGroup in
            // Loop through operations array
            for operation in demoOperation {
                
                // Add child task to task group
                taskGroup.addTask {
                    
                    // Execute slow operation
                    guard let value = try? await operation.execute() else {
                        return nil
                    }
                    
                    // Return child task result
                    return (operation.name, value)
                }
            }
            
            // Collect results of all child task in a dictionary
            var childTaskResults = [String: Double]()
            
            for await result in taskGroup.compactMap({ $0 }) {
                // Set operation name as key and operation result as value
                childTaskResults[result.0] = result.1
            }
            // All child tasks finish running, thus return task group result
            return childTaskResults
        })
        
        print("Task group completed: \(allResult)")
    }
    

    Để trả về result tổng của tất cả task con, mình sử dụng compactMap để loại bỏ toàn bộ giá trị nil được trả về từ task con.

    Khi chạy đoạn code trên, đây sẽ là output:

       

    4-Tổng kết

    Như vậy, với 2 bài viết của mình, mọi người đã có những cái nhìn và hiểu biết rõ ràng hơn về task group, một trong những update mới và quan trọng của swift 5.5 ?

    Happy coding!!

  • Python Deep Dive: Hiểu closures, decorators và các ứng dụng của chúng – Phần 2

    Python Deep Dive: Hiểu closures, decorators và các ứng dụng của chúng – Phần 2

    Trong lập trình với Python thì Functional Programming đóng một vai trò vô cùng quan trọng và các functions trong Python là các first-class citizens. Điều đó có nghĩa là chúng ta có thể vận hành các functions giống như các objects khác:

    • Truyền các function giống như các đối số.
    • Gán một function cho một biến số.
    • Return một function từ một function khác.

    Dựa trên những điều này, Python hỗ trợ một kỹ thuật vô cùng mạnh mẽ: closures. Sau khi hiểu closures, chúng ta sẽ đi đến tiếp cận một khái niệm rất quan trọng khác – decorators. Đây là 2 khái niệm/kỹ thuật mà bất kỳ lập trình viên Python chuyên nghiệp nào cũng cần phải nắm vững.

    Bài viết này yêu cầu kiến thức tiên quyết về scopes, namespace trong Python. Nếu bạn chưa tự tin, vui lòng đọc trước Phần 1

    Table of contents

    Free Variables and Closures

    Nhắc lại rằng: Các functions được xác định bên trong function khác có thể truy cập các biến bên ngoài (nonlocal)

    def outer():
        x = 'python'
        def inner():
            # x trỏ đến cùng một object mà biến x bên ngoài trỏ tới.
            print("{0} rocks!".format(x))
        inner()
    outer() # python rocks! --> Đây được gọi là một closure.

    Biến nonlocal x trong hàm inner được gọi là free variable. Khi chúng ta xem xét hàm inner, chúng ta thực sự đang thấy:

    • hàm inner
    • free variable x (đang có giá trị là ‘python’)

    Xin lưu ý rằng biến x trong hàm inner không thuộc local scope của hàm đó, mà nó nằm ở một nơi khác. Nhãn x này và nhãn x thuộc hàm outer liên kết lại với nhau, được gọi là closure.

    Returning the inner function

    Vậy điều gì sẽ xảy ra nếu như chúng ta không gọi hàm inner() bên trong hàm outer() mà thay vào đó, ta return nó. Khi gọi hàm outer(), hàm inner sẽ được tạo, và outer trả về hàm inner. Khi đó, closure nói trên vẫn đang còn tồn tại, chúng không bị mất đi. Vì vậy, khi gọi hàm outer(), trả về hàm inner, thực sự là chúng ta đang trả về một closure.
    Chúng ta có thể gán giá trị trả về từ hàm outer() cho một tên biến, ví dụ:

    fn = outer() # fn là closure
    fn() # python rocks!

    Khi chúng ta gọi hàm fn(), tại thời điểm đó – Python xác định giá trị của x trong một extended scope. Lưu ý rằng, hàm outer() đã chạy xong và đã kết thúc trước khi gọi hàm fn() –> scope của hàm outer đã được giải phóng. Vậy tại sao khi gọi hàm fn(), chúng ta vẫn nhận về được giá trị ‘python rocks!’ !!? –> closure.
    Thật magic! Để hiểu rõ hơn về closure, bạn hãy uống một chén trà rồi ngồi đọc tiếp nhé ;).

    Python Cells and Multi-Scoped Variables

    Xét ví dụ đơn giản sau:

    def outer():
        x = 'tuanh'
        def inner():
            print(x)
        return inner

    Giá trị của biến x được chia sẻ giữa 2 scope:

    • outer
    • closure

    Nhãn (label, name) x nằm trong 2 scope khác nhau nhưng luôn luôn refer tới cùng 1 giá trị. Python làm điều này bằng cách sử dụng một đối tượng trung gian, cell object.

    cell object đóng vai trò trung gian, và x sẽ tham chiếu gián tiếp đến đối tượng có giá trị ‘tuanh’. Trên thực tế, cả 2 biến x (trong outer và inner) đều trỏ đến cùng một cell object. Và khi chúng ta request giá trị của biến, Python thực hiện “double-hop” để lấy về giá trị cuối cùng.
    Bây giờ, chúng ta đã hiểu tại sao khi hàm outer() kết thúc, chúng ta vẫn có thể lấy được giá trị của biến x trong hàm inner rồi chứ.

    Closures

    Đến đây, chúng ta có thể nghĩ về closure như là một function + extended scope – scope mà chứa free variables.
    Giá trị của free variable là object mà cell trỏ tới. Mỗi khi function trong closure được gọi và free variable được tham chiếu:

    • Python tìm kiếm cell object, và sau đó bất kỳ cái gì cell đang trỏ tới.
    Nguồn: Fred Baptiste (Python Deep Dive – Functional)

    Introspection

    Chúng ta tiếp tục sử dụng ví dụ như trước:

    Nguồn: Fred Baptiste (Python Deep Dive – Functional)

    (more…)