हम दिखावा करने जा रहे हैं कि हमारे पास एक वास्तविक उत्पाद है जिसमें हमें सुधार करने की आवश्यकता है। हम एक डेटासेट का पता लगाएंगे और लॉजिस्टिक रिग्रेशन, आवर्ती तंत्रिका नेटवर्क और ट्रांसफार्मर जैसे विभिन्न मॉडलों को आज़माएंगे, यह देखेंगे कि वे कितने सटीक हैं, वे उत्पाद को कैसे बेहतर बनाने जा रहे हैं, वे कितनी तेजी से काम करते हैं, और क्या उन्हें डीबग करना आसान है और स्केल करें।
आप GitHub पर पूरा केस स्टडी कोड पढ़ सकते हैं और ज्यूपिटर नोटबुक व्यूअर में इंटरैक्टिव चार्ट के साथ विश्लेषण नोटबुक देख सकते हैं।
उत्साहित? चलो उसे करें!
कल्पना कीजिए कि हमारे पास एक ई-कॉमर्स वेबसाइट है। इस वेबसाइट पर विक्रेता उन वस्तुओं का विवरण अपलोड कर सकता है जिन्हें वे बेचना चाहते हैं। उन्हें वस्तुओं की श्रेणियाँ भी मैन्युअल रूप से चुननी होंगी जिससे उनकी गति धीमी हो सकती है।
हमारा कार्य आइटम विवरण के आधार पर श्रेणियों की पसंद को स्वचालित करना है। हालाँकि, गलत तरीके से स्वचालित विकल्प स्वचालितीकरण न होने से भी बदतर है, क्योंकि गलती पर ध्यान नहीं दिया जा सकता है, जिससे बिक्री में नुकसान हो सकता है। इसलिए यदि हम निश्चित नहीं हैं तो हम स्वचालित लेबल सेट न करने का विकल्प चुन सकते हैं।
इस केस स्टडी के लिए, हम इसका उपयोग करेंगे
हम नीचे कई मॉडल आर्किटेक्चर पर विचार करेंगे और शुरू करने से पहले यह तय करना हमेशा एक अच्छा अभ्यास है कि सबसे अच्छा विकल्प कैसे चुना जाए। यह मॉडल हमारे उत्पाद को कैसे प्रभावित करने वाला है? ...हमारा बुनियादी ढांचा?
जाहिर है, हमारे पास विभिन्न मॉडलों की ऑफ़लाइन तुलना करने के लिए एक तकनीकी गुणवत्ता मीट्रिक होगी। इस मामले में, हमारे पास एक बहु-वर्ग वर्गीकरण कार्य है, तो आइए एक संतुलित सटीकता स्कोर का उपयोग करें, जो असंतुलित लेबल को अच्छी तरह से संभालता है।
बेशक, किसी उम्मीदवार के परीक्षण का विशिष्ट अंतिम चरण एबी परीक्षण है - ऑनलाइन चरण, जो इस बात की बेहतर तस्वीर देता है कि ग्राहक परिवर्तन से कैसे प्रभावित होते हैं। आमतौर पर, एबी परीक्षण में ऑफ़लाइन परीक्षण की तुलना में अधिक समय लगता है, इसलिए केवल ऑफ़लाइन चरण के सर्वश्रेष्ठ उम्मीदवारों का ही परीक्षण किया जाता है। यह एक केस स्टडी है, और हमारे पास वास्तविक उपयोगकर्ता नहीं हैं, इसलिए हम एबी परीक्षण को कवर नहीं करेंगे।
एबी-परीक्षण के लिए किसी उम्मीदवार को आगे बढ़ाने से पहले हमें और क्या विचार करना चाहिए? ऑफ़लाइन चरण के दौरान हम ऑनलाइन परीक्षण का कुछ समय बचाने के लिए क्या सोच सकते हैं और यह सुनिश्चित कर सकते हैं कि हम वास्तव में सर्वोत्तम संभव समाधान का परीक्षण कर रहे हैं?
संतुलित सटीकता बढ़िया है, लेकिन यह स्कोर इस सवाल का जवाब नहीं देता है कि "मॉडल वास्तव में उत्पाद को कैसे प्रभावित करेगा?"। अधिक उत्पाद-उन्मुख स्कोर खोजने के लिए हमें यह समझना होगा कि हम मॉडल का उपयोग कैसे करने जा रहे हैं।
हमारी सेटिंग में, गलती करना कोई उत्तर न देने से भी बदतर है, क्योंकि विक्रेता को गलती पर ध्यान देना होगा और श्रेणी को मैन्युअल रूप से बदलना होगा। एक अनजानी गलती से बिक्री कम हो जाएगी और विक्रेता का उपयोगकर्ता अनुभव खराब हो जाएगा, हम ग्राहकों को खोने का जोखिम उठाते हैं।
इससे बचने के लिए, हम मॉडल के स्कोर के लिए सीमाएं चुनेंगे ताकि हम केवल 1% गलतियों की अनुमति दें। उत्पाद-उन्मुख मीट्रिक को निम्नानुसार सेट किया जा सकता है:
यदि हमारी त्रुटि सहनशीलता केवल 1% है तो हम कितने प्रतिशत वस्तुओं को स्वचालित रूप से वर्गीकृत कर सकते हैं?
सर्वोत्तम मॉडल का चयन करते समय हम इसे नीचे Automatic categorisation percentage
के रूप में संदर्भित करेंगे। पूर्ण सीमा चयन कोड यहां पाएं।
एक मॉडल को एक अनुरोध को संसाधित करने में कितना समय लगता है?
यह मोटे तौर पर हमें यह तुलना करने की अनुमति देगा कि यदि एक मॉडल को दूसरे मॉडल के मुकाबले चुना जाता है तो कार्य भार को संभालने के लिए हमें सेवा के लिए कितने अधिक संसाधनों को बनाए रखना होगा।
जब हमारा उत्पाद बढ़ने वाला है, तो दिए गए आर्किटेक्चर का उपयोग करके विकास को प्रबंधित करना कितना आसान होगा?
विकास से हमारा तात्पर्य यह हो सकता है:
क्या हमें विकास को संभालने के लिए एक मॉडल विकल्प पर पुनर्विचार करना होगा या एक साधारण पुनर्प्रशिक्षण पर्याप्त होगा?
प्रशिक्षण के दौरान और तैनाती के बाद मॉडल की त्रुटियों को डीबग करना कितना आसान होगा?
मॉडल का आकार मायने रखता है यदि:
हम बाद में देखेंगे कि उपरोक्त दोनों चीजें प्रासंगिक नहीं हैं, लेकिन यह अभी भी संक्षेप में विचार करने लायक है।
हम किसके साथ काम कर रहे हैं? आइए डेटा देखें और देखें कि क्या इसे साफ़ करने की ज़रूरत है!
डेटासेट में 2 कॉलम हैं: आइटम विवरण और श्रेणी, कुल 50.5k पंक्तियाँ।
file_name = "ecommerceDataset.csv" data = pd.read_csv(file_name, header=None) data.columns = ["category", "description"] print("Rows, cols:", data.shape) # >>> Rows, cols: (50425, 2)
प्रत्येक आइटम को उपलब्ध 4 श्रेणियों में से 1 सौंपा गया है: Household
, Books
, Electronics
या Clothing & Accessories
। यहां प्रति श्रेणी 1 आइटम विवरण उदाहरण दिया गया है:
घरेलू एसपीके होम डेकोर क्ले हस्तनिर्मित वॉल हैंगिंग फेस (मल्टी कलर, H35xW12cm) इस हस्तनिर्मित टेराकोटा इंडियन फेस मास्क वॉल हैंगिंग के साथ अपने घर को और अधिक सुंदर बनाएं, इससे पहले कि आप बाजार में इस हस्तनिर्मित चीज को नहीं देख सकें। आप इसे अपने लिविंग रूम/प्रवेश लॉबी में जोड़ सकते हैं।
पुस्तकें BEGF101/FEG1-अंग्रेजी में फाउंडेशन कोर्स-1 (नीरज प्रकाशन 2018 संस्करण) BEGF101/FEG1-अंग्रेजी में फाउंडेशन कोर्स-1
कपड़े और सहायक उपकरण ब्रॉडस्टार महिला डेनिम डंगरी, ब्रॉडस्टार द्वारा डंगरी पहनकर ऑल-एक्सेस पास अर्जित करें। डेनिम से बनी ये डंगरी आपको आरामदायक रखेंगी। अपने कैज़ुअल लुक को पूरा करने के लिए इन्हें सफ़ेद या काले रंग के टॉप के साथ पहनें।
इलेक्ट्रॉनिक्स कैप्रिगो हैवी ड्यूटी - 2 फीट प्रीमियम प्रोजेक्टर सीलिंग माउंट स्टैंड ब्रैकेट (एडजस्टेबल - सफेद - वजन क्षमता 15 किलोग्राम)
डेटासेट में केवल एक खाली मान है, जिसे हम हटाने जा रहे हैं।
print(data.info()) # <class 'pandas.core.frame.DataFrame'> # RangeIndex: 50425 entries, 0 to 50424 # Data columns (total 2 columns): # # Column Non-Null Count Dtype # --- ------ -------------- ----- # 0 category 50425 non-null object # 1 description 50424 non-null object # dtypes: object(2) # memory usage: 788.0+ KB data.dropna(inplace=True)
हालाँकि, बहुत सारे दोहराए गए विवरण हैं। सौभाग्य से सभी डुप्लिकेट एक ही श्रेणी के हैं, इसलिए हम उन्हें सुरक्षित रूप से हटा सकते हैं।
repeated_messages = data \ .groupby("description", as_index=False) \ .agg( n_repeats=("category", "count"), n_unique_categories=("category", lambda x: len(np.unique(x))) ) repeated_messages = repeated_messages[repeated_messages["n_repeats"] > 1] print(f"Count of repeated messages (unique): {repeated_messages.shape[0]}") print(f"Total number: {repeated_messages['n_repeats'].sum()} out of {data.shape[0]}") # >>> Count of repeated messages (unique): 13979 # >>> Total number: 36601 out of 50424
डुप्लिकेट को हटाने के बाद हमारे पास 55% मूल डेटासेट बचता है। डेटासेट अच्छी तरह से संतुलित है.
data.drop_duplicates(inplace=True) print(f"New dataset size: {data.shape}") print(data["category"].value_counts()) # New dataset size: (27802, 2) # Household 10564 # Books 6256 # Clothing & Accessories 5674 # Electronics 5308 # Name: category, dtype: int64
ध्यान दें, डेटासेट विवरण के अनुसार,
डेटासेट को भारतीय ई-कॉमर्स प्लेटफॉर्म से हटा दिया गया है।
विवरण आवश्यक रूप से अंग्रेजी में नहीं लिखे गए हैं। उनमें से कुछ गैर-ASCII प्रतीकों का उपयोग करके हिंदी या अन्य भाषाओं में लिखे गए हैं या लैटिन वर्णमाला में लिप्यंतरित हैं, या भाषाओं के मिश्रण का उपयोग करते हैं। Books
श्रेणी से उदाहरण:
यू जी सी – नेट जूनियर रिसर्च फैलोशिप एवं सहायक प्रोफेसर योग्यता …
Prarambhik Bhartiy Itihas
History of NORTH INDIA/வட இந்திய வரலாறு/ …
विवरण में गैर-अंग्रेजी शब्दों की उपस्थिति का मूल्यांकन करने के लिए, आइए 2 अंकों की गणना करें:
Word2Vec-300
में अंग्रेजी कॉर्पस पर प्रशिक्षित हैं।
ASCII-स्कोर का उपयोग करके हम सीखते हैं कि केवल 2.3% विवरणों में 1% से अधिक गैर-ASCII प्रतीक शामिल हैं।
def get_ascii_score(description): total_sym_cnt = 0 ascii_sym_cnt = 0 for sym in description: total_sym_cnt += 1 if sym.isascii(): ascii_sym_cnt += 1 return ascii_sym_cnt / total_sym_cnt data["ascii_score"] = data["description"].apply(get_ascii_score) data[data["ascii_score"] < 0.99].shape[0] / data.shape[0] # >>> 0.023
वैध अंग्रेजी शब्दों के स्कोर से पता चलता है कि केवल 1.5% विवरणों में ASCII शब्दों के बीच 70% से कम वैध अंग्रेजी शब्द हैं।
w2v_eng = gensim.models.KeyedVectors.load_word2vec_format(w2v_path, binary=True) def get_valid_eng_score(description): description = re.sub("[^az \t]+", " ", description.lower()) total_word_cnt = 0 eng_word_cnt = 0 for word in description.split(): total_word_cnt += 1 if word.lower() in w2v_eng: eng_word_cnt += 1 return eng_word_cnt / total_word_cnt data["eng_score"] = data["description"].apply(get_valid_eng_score) data[data["eng_score"] < 0.7].shape[0] / data.shape[0] # >>> 0.015
इसलिए अधिकांश विवरण (लगभग 96%) अंग्रेजी में या अधिकतर अंग्रेजी में हैं। हम अन्य सभी विवरण हटा सकते हैं, लेकिन इसके बजाय, आइए उन्हें वैसे ही छोड़ दें और फिर देखें कि प्रत्येक मॉडल उन्हें कैसे संभालता है।
आइए अपने डेटासेट को 3 समूहों में विभाजित करें:
प्रशिक्षण 70% - मॉडलों के प्रशिक्षण के लिए (19 हजार संदेश)
परीक्षण 15% - पैरामीटर और थ्रेशोल्ड चयन के लिए (4.1k संदेश)
मूल्यांकन 15% - अंतिम मॉडल चुनने के लिए (4.1k संदेश)
from sklearn.model_selection import train_test_split data_train, data_test = train_test_split(data, test_size=0.3) data_test, data_eval = train_test_split(data_test, test_size=0.5) data_train.shape, data_test.shape, data_eval.shape # >>> ((19461, 3), (4170, 3), (4171, 3))
एक अच्छी आधार रेखा प्राप्त करने के लिए शुरुआत में कुछ सीधा और तुच्छ कार्य करना सहायक होता है। आइए आधार रेखा के रूप में ट्रेन डेटासेट के आधार पर शब्दों की संरचना का एक बैग बनाएं।
आइए शब्दकोश का आकार भी 100 शब्दों तक सीमित रखें।
count_vectorizer = CountVectorizer(max_features=100, stop_words="english") x_train_baseline = count_vectorizer.fit_transform(data_train["description"]) y_train_baseline = data_train["category"] x_test_baseline = count_vectorizer.transform(data_test["description"]) y_test_baseline = data_test["category"] x_train_baseline = x_train_baseline.toarray() x_test_baseline = x_test_baseline.toarray()
मैं एक मॉडल के रूप में लॉजिस्टिक रिग्रेशन का उपयोग करने की योजना बना रहा हूं, इसलिए मुझे प्रशिक्षण से पहले काउंटर सुविधाओं को सामान्य करने की आवश्यकता है।
ss = StandardScaler() x_train_baseline = ss.fit_transform(x_train_baseline) x_test_baseline = ss.transform(x_test_baseline) lr = LogisticRegression() lr.fit(x_train_baseline, y_train_baseline) balanced_accuracy_score(y_test_baseline, lr.predict(x_test_baseline)) # >>> 0.752
मल्टी-क्लास लॉजिस्टिक रिग्रेशन ने 75.2% संतुलित सटीकता दिखाई। यह एक महान आधार रेखा है!
हालाँकि समग्र वर्गीकरण गुणवत्ता बढ़िया नहीं है, फिर भी मॉडल हमें कुछ अंतर्दृष्टि दे सकता है। आइए पूर्वानुमानित लेबलों की संख्या द्वारा सामान्यीकृत भ्रम मैट्रिक्स को देखें। एक्स-अक्ष अनुमानित श्रेणी को दर्शाता है, और वाई-अक्ष वास्तविक श्रेणी को दर्शाता है। प्रत्येक कॉलम को देखकर हम वास्तविक श्रेणियों का वितरण देख सकते हैं जब एक निश्चित श्रेणी की भविष्यवाणी की गई थी।
उदाहरण के लिए, Electronics
अक्सर Household
समझ लिया जाता है। लेकिन यह साधारण मॉडल भी Clothing & Accessories
काफी सटीकता से कैप्चर कर सकता है।
Clothing & Accessories
श्रेणी की भविष्यवाणी करते समय यहां फीचर महत्व दिए गए हैं:
Clothing & Accessories
श्रेणी के पक्ष और विपक्ष में शीर्ष-6 सर्वाधिक योगदान देने वाले शब्द:
women 1.49 book -2.03 men 0.93 table -1.47 cotton 0.92 author -1.11 wear 0.69 books -1.10 fit 0.40 led -0.90 stainless 0.36 cable -0.85
आइए अब अधिक उन्नत मॉडलों पर विचार करें, जो विशेष रूप से अनुक्रमों - आवर्ती तंत्रिका नेटवर्क के साथ काम करने के लिए डिज़ाइन किए गए हैं। जीआरयू और एलएसटीएम साधारण आरएनएन में होने वाले विस्फोट ग्रेडिएंट्स से लड़ने के लिए सामान्य उन्नत परतें हैं।
हम विवरणों को टोकनाइज़ करने और एक मॉडल बनाने और प्रशिक्षित करने के लिए pytorch
लाइब्रेरी का उपयोग करेंगे।
सबसे पहले, हमें टेक्स्ट को संख्याओं में बदलना होगा:
ट्रेन डेटासेट को टोकनाइज़ करने से हमें जो शब्दावली मिलती है वह बड़ी है - लगभग 90k शब्द। हमारे पास जितने अधिक शब्द होंगे, मॉडल को सीखने के लिए उतना ही बड़ा एम्बेडिंग स्थान होगा। प्रशिक्षण को सरल बनाने के लिए, आइए इसमें से दुर्लभतम शब्दों को हटा दें और केवल उन्हें छोड़ दें जो कम से कम 3% विवरणों में दिखाई देते हैं। इससे शब्दावली घटकर 340 शब्दों तक रह जाएगी।
(पूर्ण CorpusDictionary
कार्यान्वयन यहां पाएं)
corpus_dict = util.CorpusDictionary(data_train["description"]) corpus_dict.truncate_dictionary(min_frequency=0.03) data_train["vector"] = corpus_dict.transform(data_train["description"]) data_test["vector"] = corpus_dict.transform(data_test["description"]) print(data_train["vector"].head()) # 28453 [1, 1, 1, 1, 12, 1, 2, 1, 6, 1, 1, 1, 1, 1, 6,... # 48884 [1, 1, 13, 34, 3, 1, 1, 38, 12, 21, 2, 1, 37, ... # 36550 [1, 60, 61, 1, 62, 60, 61, 1, 1, 1, 1, 10, 1, ... # 34999 [1, 34, 1, 1, 75, 60, 61, 1, 1, 72, 1, 1, 67, ... # 19183 [1, 83, 1, 1, 87, 1, 1, 1, 12, 21, 42, 1, 2, 1... # Name: vector, dtype: object
अगली चीज़ जो हमें तय करने की ज़रूरत है वह वेक्टर की सामान्य लंबाई है जिसे हम आरएनएन में इनपुट के रूप में फीड करने जा रहे हैं। हम पूर्ण वैक्टर का उपयोग नहीं करना चाहते, क्योंकि सबसे लंबे विवरण में 9.4k टोकन हैं।
हालाँकि, ट्रेन डेटासेट में 95% विवरण अब 352 टोकन से अधिक नहीं हैं - यह ट्रिमिंग के लिए एक अच्छी लंबाई है। छोटे विवरणों से क्या होगा?
वे सामान्य लंबाई तक पैडिंग इंडेक्स के साथ गद्देदार होने जा रहे हैं।
print(max(data_train["vector"].apply(len))) # >>> 9388 print(int(np.quantile(data_train["vector"].apply(len), q=0.95))) # >>> 352
अगला - हमें नुकसान की गणना करने और प्रत्येक प्रशिक्षण चरण पर बैक-प्रचार करने के लिए लक्ष्य श्रेणियों को 0-1 वैक्टर में बदलने की आवश्यकता है।
def get_target(label, total_labels=4): target = [0] * total_labels target[label_2_idx.get(label)] = 1 return target data_train["target"] = data_train["category"].apply(get_target) data_test["target"] = data_test["category"].apply(get_target)
अब हम मॉडल में फीड करने के लिए एक कस्टम pytorch
डेटासेट और डेटालोडर बनाने के लिए तैयार हैं। पूर्ण PaddedTextVectorDataset
कार्यान्वयन यहां पाएं।
ds_train = util.PaddedTextVectorDataset( data_train["description"], data_train["target"], corpus_dict, max_vector_len=352, ) ds_test = util.PaddedTextVectorDataset( data_test["description"], data_test["target"], corpus_dict, max_vector_len=352, ) train_dl = DataLoader(ds_train, batch_size=512, shuffle=True) test_dl = DataLoader(ds_test, batch_size=512, shuffle=False)
अंत में, चलिए एक मॉडल बनाते हैं।
न्यूनतम वास्तुकला है:
मापदंडों के छोटे मूल्यों (एम्बेडिंग वेक्टर का आकार, आरएनएन में छिपी परत का आकार, आरएनएन परतों की संख्या) से शुरू करके और कोई नियमितीकरण नहीं होने पर, हम धीरे-धीरे मॉडल को और अधिक जटिल बना सकते हैं जब तक कि यह ओवर-फिटिंग के मजबूत संकेत न दिखाए, और फिर संतुलन बनाएं नियमितीकरण (आरएनएन परत में और अंतिम रैखिक परत से पहले ड्रॉपआउट)।
class GRU(nn.Module): def __init__(self, vocab_size, embedding_dim, n_hidden, n_out): super().__init__() self.vocab_size = vocab_size self.embedding_dim = embedding_dim self.n_hidden = n_hidden self.n_out = n_out self.emb = nn.Embedding(self.vocab_size, self.embedding_dim) self.gru = nn.GRU(self.embedding_dim, self.n_hidden) self.dropout = nn.Dropout(0.3) self.out = nn.Linear(self.n_hidden, self.n_out) def forward(self, sequence, lengths): batch_size = sequence.size(1) self.hidden = self._init_hidden(batch_size) embs = self.emb(sequence) embs = pack_padded_sequence(embs, lengths, enforce_sorted=True) gru_out, self.hidden = self.gru(embs, self.hidden) gru_out, lengths = pad_packed_sequence(gru_out) dropout = self.dropout(self.hidden[-1]) output = self.out(dropout) return F.log_softmax(output, dim=-1) def _init_hidden(self, batch_size): return Variable(torch.zeros((1, batch_size, self.n_hidden)))
हम हानि फ़ंक्शन के रूप में Adam
ऑप्टिमाइज़र और cross_entropy
उपयोग करेंगे।
vocab_size = len(corpus_dict.word_to_idx) emb_dim = 4 n_hidden = 15 n_out = len(label_2_idx) model = GRU(vocab_size, emb_dim, n_hidden, n_out) opt = optim.Adam(model.parameters(), 1e-2) util.fit( model=model, train_dl=train_dl, test_dl=test_dl, loss_fn=F.cross_entropy, opt=opt, epochs=35 ) # >>> Train loss: 0.3783 # >>> Val loss: 0.4730
इस मॉडल ने इवल डेटासेट पर 84.3% संतुलित सटीकता दिखाई। वाह, क्या प्रगति है!
आरएनएन मॉडल को शुरू से प्रशिक्षित करने का प्रमुख नकारात्मक पक्ष यह है कि इसमें शब्दों का अर्थ स्वयं सीखना पड़ता है - यही एम्बेडिंग परत का काम है। पूर्व-प्रशिक्षित word2vec
मॉडल रेडी-मेड एम्बेडिंग परत के रूप में उपयोग करने के लिए उपलब्ध हैं, जो मापदंडों की संख्या को कम करता है और टोकन में बहुत अधिक अर्थ जोड़ता है। आइए pytorch
में उपलब्ध word2vec
मॉडलों में से एक का उपयोग करें - glove, dim=300
।
हमें डेटासेट निर्माण में केवल मामूली बदलाव करने की आवश्यकता है - अब हम प्रत्येक विवरण और मॉडल आर्किटेक्चर glove
लिए पूर्व-परिभाषित इंडेक्स का एक वेक्टर बनाना चाहते हैं।
ds_emb_train = util.PaddedTextVectorDataset( data_train["description"], data_train["target"], emb=glove, max_vector_len=max_len, ) ds_emb_test = util.PaddedTextVectorDataset( data_test["description"], data_test["target"], emb=glove, max_vector_len=max_len, ) dl_emb_train = DataLoader(ds_emb_train, batch_size=512, shuffle=True) dl_emb_test = DataLoader(ds_emb_test, batch_size=512, shuffle=False)
import torchtext.vocab as vocab glove = vocab.GloVe(name='6B', dim=300) class LSTMPretrained(nn.Module): def __init__(self, n_hidden, n_out): super().__init__() self.emb = nn.Embedding.from_pretrained(glove.vectors) self.emb.requires_grad_ = False self.embedding_dim = 300 self.n_hidden = n_hidden self.n_out = n_out self.lstm = nn.LSTM(self.embedding_dim, self.n_hidden, num_layers=1) self.dropout = nn.Dropout(0.5) self.out = nn.Linear(self.n_hidden, self.n_out) def forward(self, sequence, lengths): batch_size = sequence.size(1) self.hidden = self.init_hidden(batch_size) embs = self.emb(sequence) embs = pack_padded_sequence(embs, lengths, enforce_sorted=True) lstm_out, (self.hidden, _) = self.lstm(embs) lstm_out, lengths = pad_packed_sequence(lstm_out) dropout = self.dropout(self.hidden[-1]) output = self.out(dropout) return F.log_softmax(output, dim=-1) def init_hidden(self, batch_size): return Variable(torch.zeros((1, batch_size, self.n_hidden)))
और हम प्रशिक्षण के लिए तैयार हैं!
n_hidden = 50 n_out = len(label_2_idx) emb_model = LSTMPretrained(n_hidden, n_out) opt = optim.Adam(emb_model.parameters(), 1e-2) util.fit(model=emb_model, train_dl=dl_emb_train, test_dl=dl_emb_test, loss_fn=F.cross_entropy, opt=opt, epochs=11)
अब हमें eval डेटासेट पर 93.7% संतुलित सटीकता मिल रही है। वाह!
अनुक्रमों के साथ काम करने के लिए आधुनिक अत्याधुनिक मॉडल ट्रांसफार्मर हैं। हालाँकि, एक ट्रांसफार्मर को शुरू से प्रशिक्षित करने के लिए, हमें भारी मात्रा में डेटा और कम्प्यूटेशनल संसाधनों की आवश्यकता होगी। हम यहां जो प्रयास कर सकते हैं - वह है अपने उद्देश्य की पूर्ति के लिए पूर्व-प्रशिक्षित मॉडलों में से एक को बेहतर बनाना। ऐसा करने के लिए हमें एक पूर्व-प्रशिक्षित BERT मॉडल डाउनलोड करना होगा और अंतिम भविष्यवाणी प्राप्त करने के लिए ड्रॉपआउट और रैखिक परत जोड़ना होगा। 4 युगों के लिए एक ट्यून किए गए मॉडल को प्रशिक्षित करने की अनुशंसा की जाती है। मैंने समय बचाने के लिए केवल 2 अतिरिक्त युगों का प्रशिक्षण लिया - ऐसा करने में मुझे 40 मिनट लगे।
from transformers import BertModel class BERTModel(nn.Module): def __init__(self, n_out=12): super(BERTModel, self).__init__() self.l1 = BertModel.from_pretrained('bert-base-uncased') self.l2 = nn.Dropout(0.3) self.l3 = nn.Linear(768, n_out) def forward(self, ids, mask, token_type_ids): output_1 = self.l1(ids, attention_mask = mask, token_type_ids = token_type_ids) output_2 = self.l2(output_1.pooler_output) output = self.l3(output_2) return output
ds_train_bert = bert.get_dataset( list(data_train["description"]), list(data_train["target"]), max_vector_len=64 ) ds_test_bert = bert.get_dataset( list(data_test["description"]), list(data_test["target"]), max_vector_len=64 ) dl_train_bert = DataLoader(ds_train_bert, sampler=RandomSampler(ds_train_bert), batch_size=batch_size) dl_test_bert = DataLoader(ds_test_bert, sampler=SequentialSampler(ds_test_bert), batch_size=batch_size)
b_model = bert.BERTModel(n_out=4) b_model.to(torch.device("cpu")) def loss_fn(outputs, targets): return torch.nn.BCEWithLogitsLoss()(outputs, targets) optimizer = optim.AdamW(b_model.parameters(), lr=2e-5, eps=1e-8) epochs = 2 scheduler = get_linear_schedule_with_warmup( optimizer, num_warmup_steps=0, num_training_steps=total_steps ) bert.fit(b_model, dl_train_bert, dl_test_bert, optimizer, scheduler, loss_fn, device, epochs=epochs) torch.save(b_model, "models/bert_fine_tuned")
प्रशिक्षण लॉग:
2024-02-29 19:38:13.383953 Epoch 1 / 2 Training... 2024-02-29 19:40:39.303002 step 40 / 305 done 2024-02-29 19:43:04.482043 step 80 / 305 done 2024-02-29 19:45:27.767488 step 120 / 305 done 2024-02-29 19:47:53.156420 step 160 / 305 done 2024-02-29 19:50:20.117272 step 200 / 305 done 2024-02-29 19:52:47.988203 step 240 / 305 done 2024-02-29 19:55:16.812437 step 280 / 305 done 2024-02-29 19:56:46.990367 Average training loss: 0.18 2024-02-29 19:56:46.990932 Validating... 2024-02-29 19:57:51.182859 Average validation loss: 0.10 2024-02-29 19:57:51.182948 Epoch 2 / 2 Training... 2024-02-29 20:00:25.110818 step 40 / 305 done 2024-02-29 20:02:56.240693 step 80 / 305 done 2024-02-29 20:05:25.647311 step 120 / 305 done 2024-02-29 20:07:53.668489 step 160 / 305 done 2024-02-29 20:10:33.936778 step 200 / 305 done 2024-02-29 20:13:03.217450 step 240 / 305 done 2024-02-29 20:15:28.384958 step 280 / 305 done 2024-02-29 20:16:57.004078 Average training loss: 0.08 2024-02-29 20:16:57.004657 Validating... 2024-02-29 20:18:01.546235 Average validation loss: 0.09
अंत में, फाइन ट्यून किया गया BERT मॉडल ईवल डेटासेट पर 95.1% संतुलित सटीकता दिखाता है।
अंतिम सुविज्ञ विकल्प चुनने के लिए हमने पहले ही विचार करने योग्य बातों की एक सूची बना ली है।
यहां मापने योग्य पैरामीटर दिखाने वाले चार्ट हैं:
हालाँकि फाइन-ट्यून BERT गुणवत्ता में अग्रणी है, पूर्व-प्रशिक्षित एम्बेडिंग परत LSTM+EMB
के साथ RNN दूसरे स्थान पर है, जो स्वचालित श्रेणी असाइनमेंट से केवल 3% पीछे है।
दूसरी ओर, फाइन-ट्यून किए गए BERT का अनुमान समय LSTM+EMB
से 14 गुना अधिक है। इससे बैकएंड रखरखाव लागत में वृद्धि होगी जो संभवत LSTM+EMB
तुलना में फाइन-ट्यून किए गए BERT
मिलने वाले लाभों से अधिक होगी।
जहां तक इंटरऑपरेबिलिटी का सवाल है, हमारा बेसलाइन लॉजिस्टिक रिग्रेशन मॉडल अब तक सबसे अधिक व्याख्या योग्य है और इस संबंध में कोई भी तंत्रिका नेटवर्क इससे हार जाता है। साथ ही, बेसलाइन शायद सबसे कम स्केलेबल है - श्रेणियां जोड़ने से बेसलाइन की पहले से ही कम गुणवत्ता कम हो जाएगी।
भले ही BERT अपनी उच्च सटीकता के साथ सुपरस्टार की तरह लगता है, हम पूर्व-प्रशिक्षित एम्बेडिंग परत के साथ RNN के साथ जाते हैं। क्यों? यह काफी सटीक है, बहुत धीमा नहीं है, और जब चीजें बड़ी हो जाती हैं तो इसे संभालना बहुत जटिल नहीं होता है।
आशा है आपको यह केस स्टडी पसंद आयी होगी। आपने कौन सा मॉडल चुना होगा और क्यों?