"يبدو أن هذا حدث بالفعل؟" البحث عن حوادث ومطالبات مماثلة

كل من قضى وقتًا معينًا في دعم الأنظمة على دراية بالشعور بـ déjà vu عندما تلقوا تطبيقًا جديدًا: "لقد كان الأمر كذلك ، تم حلها ، لكنني لا أتذكر كيف بالضبط". يمكنك قضاء بعض الوقت ، والتصفح في التطبيقات السابقة ومحاولة العثور على تطبيقات مماثلة. سيساعد ذلك: سيتم إغلاق الحادث بشكل أسرع ، أو قد يكون من الممكن اكتشاف السبب الجذري وإغلاق المشكلة مرة واحدة وإلى الأبد.


الموظفون "الشباب" الذين انضموا للتو إلى الفريق ليس لديهم مثل هذه القصة في رؤوسهم. على الأرجح ، أنهم لا يعرفون أن حادثة مماثلة ، على سبيل المثال ، وقعت قبل ستة أشهر إلى سنة. وقرر الزميل من الغرفة المجاورة هذا الحادث.


على الأرجح ، لن يبحث الموظفون "الشباب" عن شيء مشابه في قاعدة بيانات الحوادث ، لكنهم سيحلون المشاكل من الصفر. قضاء المزيد من الوقت ، واكتساب الخبرة ، والتكيف في المرة القادمة بشكل أسرع. أو ربما ينسون ذلك على الفور ضمن مجموعة التطبيقات الجديدة. وفي المرة القادمة سوف يحدث كل شيء مرة أخرى.


نحن نستخدم بالفعل نماذج ML لتصنيف الحوادث . لمساعدة فريقنا في معالجة التطبيقات بشكل أكثر كفاءة ، أنشأنا نموذج ML آخر لإعداد قائمة من "حوادث مماثلة مغلقة سابقًا". التفاصيل - تحت الخفض.


ماذا نحتاج؟


لكل حادث وارد ، من الضروري إيجاد حوادث مغلقة "مماثلة" في التاريخ. يجب أن يحدث تعريف "التشابه" في بداية الحادث ، ويفضل قبل أن يبدأ فريق الدعم التحليل.


لمقارنة الحوادث ، من الضروري استخدام المعلومات التي يقدمها المستخدم عند الاتصال: وصف موجز ووصف تفصيلي (إن وجد) ، وأية سمات لسجل المستخدم.


يدعم الفريق 4 مجموعات من الأنظمة. يبلغ إجمالي عدد الحوادث التي أريد استخدامها للبحث عن حوادث مماثلة حوالي 10 آلاف حادث.


القرار الأول


لا توجد معلومات مؤكدة عن "تشابه" الحوادث التي تحدث. لذلك ، يجب تأجيل الخيارات الحديثة لتدريب شبكات سيامي في الوقت الحالي.
أول ما يتبادر إلى الذهن هو تجميع بسيط لـ "حقيبة كلمات" مكونة من محتويات الطعون.


في هذه الحالة ، تكون عملية التعامل مع الحوادث كما يلي:


  1. تسليط الضوء على أجزاء النص اللازمة
  2. النص قبل المعالجة / التنظيف
  3. TF- جيش الدفاع الإسرائيلي vectorization
  4. البحث عن أقرب جار لك

من الواضح أنه مع النهج الموصوف ، سيتم التشابه على أساس مقارنة القواميس: استخدام نفس الكلمات أو n-gram في حادثين مختلفين سيعتبر "تشابه".


بالطبع ، هذا هو نهج مبسط إلى حد ما. لكن تذكر أننا نقوم بتقييم نصوص الزيارات للمستخدم ، إذا تم وصف المشكلة بكلمات مماثلة - على الأرجح تكون الحوادث متشابهة. بالإضافة إلى النص ، يمكنك إضافة اسم قسم المستخدم ، مع توقع أن يواجه مستخدمو نفس الإدارات في مؤسسات مختلفة مشكلات مماثلة.


تسليط الضوء على أجزاء النص اللازمة


بيانات الحوادث التي نحصل عليها من service-now.com بأبسط الطرق - من خلال تشغيل التقارير المخصصة برمجيًا واسترداد نتائجها في شكل ملفات CSV.


يتم إرجاع البيانات على الرسائل المتبادلة بين الدعم والمستخدمين كجزء من الحادث في هذه الحالة في شكل حقل نص كبير واحد ، مع سجل المراسلات بأكمله.


المعلومات المتعلقة بالنداء الأول من مثل هذا الحقل يجب أن "تقطع" بتعبيرات منتظمة.


  • جميع الرسائل مفصولة بسطر مميز <عندما> - <عند>.
  • غالبًا ما تنتهي الرسائل بتوقيعات رسمية ، خاصة إذا تم تقديم الطعن عن طريق البريد الإلكتروني. هذه المعلومات هي "fonil" بشكل ملحوظ في قائمة الكلمات الهامة ، لذلك كان لا بد من حذف التوقيع.

اتضح شيء مثل هذا:


def get_first_message(messages): res = "" if len(messages) > 0: # take the first message spl = re.split("\d{2}-\d{2}-\d{4} \d{2}:\d{2}:\d{2} - ((\w+((\s|-)\w+)?,(\s\w+)+)|\w{9}|guest)\s\(\w+\s\w+\)\n", messages.lower()) res = spl[-1] # cut off "mail footer" with finalization statements res = re.split("(best|kind)(\s)+regard(s)+", res)[0] # cut off "mail footer" with embedded pictures res = re.split("\[cid:", res)[0] # cut off "mail footer" with phone prefix res = re.split("\+(\d(\s|-)?){7}", res)[0] return res 

تجهيز النصوص الحادث


لتحسين جودة التصنيف ، تتم معالجة نص الاستئناف مسبقًا.


باستخدام مجموعة من التعبيرات العادية في أوصاف الحوادث ، تم العثور على أجزاء مميزة: التواريخ ، وأسماء الخوادم ، ورموز المنتج ، وعناوين IP ، وعناوين الويب ، وأشكال الأسماء غير الصحيحة ، إلخ. تم استبدال هذه الشظايا مع الرموز مفهوم المقابلة.


في النهاية ، تم استخدام stamming لجلب الكلمات إلى شكل شائع. هذا سمح لنا بالتخلص من صيغ الجمع ونهايات الأفعال. تم استخدام snowballstemmer المعروفة باسم آلة ثقب.


يتم دمج جميع عمليات المعالجة في فئة تحويل واحدة ، والتي يمكن استخدامها في عمليات مختلفة.


بالمناسبة ، اتضح (بشكل تجريبي ، بالطبع) أن طريقة stemmer.stemWord() ليست آمنة في سلسلة العمليات. لذلك ، إذا حاولت تنفيذ معالجة نصية متوازية داخل خط الأنابيب ، على سبيل المثال ، باستخدام joblib Prallel / مؤخر ، فيجب حماية الوصول إلى المثيل العام للنصيص مع الأقفال.


 __replacements = [ ('(\d{1,3}\.){3}\d{1,3}', 'IPV4'), ('(?<=\W)((\d{2}[-\/ \.]?){2}(19|20)\d{2})|(19|20)\d{2}([-\/ \.]?\d{2}){2}(?=\W)', 'YYYYMMDD'), ('(?<=\W)(19|20)\d{2}(?=\W)', 'YYYY'), ('(?<=\W)(0|1)?\d\s?(am|pm)(?=\W)', 'HOUR'), ('http[s]?:\/\/(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', 'SOMEURL') #      ] __stemmer_lock = threading.Lock() __stemmer = snowballstemmer.stemmer('english') def stem_string(text: str): def stem_words(word_list): with __stemmer_lock: res = __stemmer.stemWords(word_list) return res return " ".join(stem_words(text.split())) def clean_text(text: str): res = text for p in __replacements: res = re.sub(p[0], '#'+p[1]+'#', res) return res def process_record(record): txt = "" for t in record: t = "" if t == np.nan else t txt += " " + get_first_message(str(t)) return stem_string(clean_text(txt.lower())) class CommentsTextTransformer(BaseEstimator, TransformerMixin): _n_jobs = 1 def __init__(self, n_jobs=1): self._n_jobs = n_jobs def fit(self, X, y=None): return self def transform(self, X, y=None): features = Parallel(n_jobs=self._n_jobs)( delayed(process_record)(rec) for i, rec in enumerate(X.values) ) return np.array(features, dtype=object).reshape(len(X),) 

كمية موجهة


تتم عملية النقل بواسطة TfidfVectorizer القياسي TfidfVectorizer التالية:


  • max_features = 10000
  • ngram = (1،3) - في محاولة للقبض على مجموعات مستقرة والروابط الدلالية
  • max_df / min_df - اليسار افتراضيًا
  • stop_words - قائمة قياسية من الكلمات الإنجليزية ، بالإضافة إلى مجموعة الكلمات الإضافية الخاصة بها. على سبيل المثال ، ذكر بعض المستخدمين أسماء المحللين ، وفي كثير من الأحيان أصبحت الأسماء الصحيحة سمات مهمة.

TfidfVectorizer نفسه TfidfVectorizer تطبيع L2 افتراضيًا ، لذلك تكون نواقل الحوادث جاهزة لقياس المسافة بين جيب التمام.


البحث عن حوادث مماثلة


المهمة الرئيسية للعملية هي إرجاع قائمة بأقرب جيران N. فئة sklearn.neighbors.NearestNeighbors مناسبة تمامًا لهذا الغرض. مشكلة واحدة هي أنها لا تنفذ طريقة transform ، والتي بدونها لا يمكن استخدامها في pipeline .


لذلك ، كان من الضروري جعله يعتمد على Transformer ، والذي وضعه فقط في الخطوة الأخيرة من pipeline :


 class NearestNeighborsTransformer(NearestNeighbors, TransformerMixin): def __init__(self, n_neighbors=5, radius=1.0, algorithm='auto', leaf_size=30, metric='minkowski', p=2, metric_params=None, n_jobs=None, **kwargs): super(NearestNeighbors, self).__init__(n_neighbors=n_neighbors, radius=radius, algorithm=algorithm, leaf_size=leaf_size, metric=metric, p=p, metric_params=metric_params, n_jobs=n_jobs) def transform(self, X, y=None): res = self.kneighbors(X, self.n_neighbors, return_distance=True) return res 

عملية المعالجة


بوضع كل ذلك معا ، نحصل على عملية مدمجة:


 p = Pipeline( steps=[ ('grp', ColumnTransformer( transformers=[ ('text', Pipeline(steps=[ ('pp', CommentsTextTransformer(n_jobs=-1)), ("tfidf", TfidfVectorizer(stop_words=get_stop_words(), ngram_range=(1, 3), max_features=10000)) ]), ['short_description', 'comments', 'u_impacted_department'] ) ] )), ("nn", NearestNeighborsTransformer(n_neighbors=10, metric='cosine')) ], memory=None) 

بعد التدريب ، يمكن حفظ pipeline في ملف باستخدام pickle واستخدامه للتعامل مع الحوادث الواردة.
جنبًا إلى جنب مع النموذج ، سنقوم بحفظ حقول الحوادث الضرورية - من أجل استخدامها لاحقًا في الإخراج عند تشغيل النموذج.


 # inc_data - pandas.Dataframe,     # ref_data - pandas.Dataframe,    . #     .    # inc_data["recommendations_json"] = "" #   . # column_list -  ,          nn_dist, nn_refs = p.transform(inc_data[column_list]) for idx, refs in enumerate(nn_refs): nn_data = ref_data.iloc[refs][['number', 'short_description']].copy() nn_data['distance'] = nn_dist[idx] inc_data.iloc[idx]["recommendations_json"] = nn_data.to_json(orient='records') #     , .     -. inc_data[['number', 'short_description', 'recommendations_json']].to_json(out_file_name, orient='records') 

نتائج التطبيق الأول


كان رد فعل الزملاء على إدخال نظام "تلميحات" إيجابيا للغاية بشكل عام. بدأت الحوادث المتكررة يتم حلها بشكل أسرع ، وبدأنا العمل على استكشاف الأخطاء وإصلاحها.


ومع ذلك ، لا يمكن للمرء أن يتوقع معجزة من نظام التعلم دون إشراف. اشتكى الزملاء من أن النظام يقدم أحيانًا روابط غير ذات صلة تمامًا. في بعض الأحيان كان من الصعب فهم من أين تأتي هذه التوصيات.


كان من الواضح أن مجال تحسين النموذج ضخم. يمكن حل بعض أوجه القصور ، بما في ذلك أو استبعاد بعض سمات الحادث. الجزء - عن طريق اختيار مستوى قطع مناسب للمسافة بين الحادث الحالي و "التوصية". ويمكن النظر في طرق مكافحة ناقلات أخرى.


لكن المشكلة الرئيسية كانت عدم وجود مقاييس جودة للتوصيات. وإذا كان الأمر كذلك ، فقد كان من المستحيل فهم "ما هو جيد وما هو سيء ، وكم هو" ، وبناء مقارنة بين النماذج في هذا الشأن.


لم نتمكن من الوصول إلى سجلات http ، لأن نظام الخدمة يعمل عن بعد (SaaS). لقد أجرينا استطلاعات رأي المستخدمين - ولكن نوعيًا فقط. كان من الضروري المضي في التقييمات الكمية ، والبناء على أساس مقاييس الجودة الواضحة.


ولكن المزيد عن ذلك في الجزء التالي ...

Source: https://habr.com/ru/post/ar472210/


All Articles