
مرة أخرى ، عندما كنت أقود سيارة حول مدينتي وأتجول في حفرة أخرى ، اعتقدت: هل توجد مثل هذه الطرق "الجيدة" في كل مكان في بلدنا وقررت أنه ينبغي لنا تقييم الوضع بموضوعية بجودة الطرق في بلدنا.
إضفاء الطابع الرسمي على المهمة
في روسيا ، تم توضيح متطلبات جودة الطرق في GOST R 50597-2017 "الطرق والطرق. متطلبات الحالة التشغيلية مقبولة بموجب شروط ضمان السلامة على الطرق. طرق التحكم. " تحدد هذه الوثيقة متطلبات تغطية الطريق ، جوانب الطرق ، شرائط التقسيم ، الأرصفة ، ممرات المشاة ، وما إلى ذلك ، كما تحدد أنواع الضرر.
نظرًا لأن مهمة تحديد جميع معالم الطرق مهمة للغاية ، فقد قررت تضييقها بنفسي والتركيز فقط على مشكلة تحديد العيوب في تغطية الطريق. في GOST R 50597-2017 ، يتم تمييز العيوب التالية في طلاء الطريق:
- الحفر
- فواصل
- السحب
- التحولات
- أمشاط
- المسار
- التعرق الموثق
قررت معالجة هذه العيوب.
جمع البيانات
أين يمكنني الحصول على صور توضح أجزاء كبيرة بما يكفي من الطريق ، وحتى بالإشارة إلى تحديد الموقع الجغرافي؟ جاءت الإجابة في أحجار الراين - صور بانورامية على خرائط Yandex (أو Google) ، ومع ذلك ، بعد قليل من البحث ، وجدت العديد من الخيارات البديلة:
- إصدار محركات البحث عن الصور للطلبات ذات الصلة ؛
- الصور على مواقع لتلقي الشكاوى (Rosyama ، المواطن الغاضب ، الفضيلة ، إلخ.)
- دفع Opendatascience مشروعًا لاكتشاف عيوب الطرق باستخدام مجموعة بيانات ملحوظة - github.com/sekilab/RoadDamageDetector
لسوء الحظ ، أظهر تحليل لهذه الخيارات أنها ليست مناسبة جدًا بالنسبة لي: إصدار محركات البحث لديه الكثير من الضوضاء (العديد من الصور التي ليست طرقًا أو عروض مختلفة أو ما إلى ذلك) ، والصور من مواقع تلقي الشكاوى تحتوي فقط على صور بها انتهاكات كبيرة لسطح الأسفلت ، هناك عدد غير قليل من الصور مع انتهاكات صغيرة للتغطية وبدون انتهاكات على هذه المواقع ، يتم جمع مجموعة البيانات من مشروع RoadDamageDetector في اليابان ولا تحتوي على عينات ذات انتهاكات كبيرة للتغطية ، وكذلك الطرق دون تغطية على الإطلاق.
نظرًا لأن الخيارات البديلة غير مناسبة ، فسنستخدم بانوراما Yandex (لقد استبعدت خيار بانوراما Google ، نظرًا لأن الخدمة مقدمة في عدد أقل من المدن في روسيا ويتم تحديثها بشكل أقل تكرارًا). قرر جمع البيانات في المدن التي يبلغ عدد سكانها أكثر من 100 ألف شخص ، وكذلك في المراكز الفيدرالية. لقد قدمت قائمة بأسماء المدن - كان هناك 176 منهم ، فيما بعد تبين أن 149 منهم فقط لديهم صور بانورامية. لن أتطرق إلى ميزات تحليل البلاط ، سأقول أنه في النهاية حصلت على 149 مجلدًا (مجلد لكل مدينة) بلغ إجمالي عدد الصور بها 1.7 مليون صورة. على سبيل المثال ، بالنسبة إلى Novokuznetsk ، بدا المجلد كما يلي:

بعدد الصور التي تم تنزيلها ، تم توزيع المدن على النحو التالي:
الجدولالمدينة
| عدد الصور ، أجهزة الكمبيوتر
|
---|
موسكو
| 86048
|
سانت بطرسبرغ
| 41376
|
سارانسك
| 18880
|
بودولسك
| 18560
|
كراسنوجورسك
| 18208
|
ليوبرتسي
| 17760
|
كالينينجراد
| 16928
|
Kolomna
| 16832
|
ميتيشتشي
| 16192
|
فلاديفوستوك
| 16096
|
بالشيخة
| 15968
|
بتروزافودسك
| 15968
|
ايكاترينبرغ
| 15808
|
فيليكي نوفغورود
| 15744
|
نابريجني تشلني
| 15680
|
كراسنودار
| 15520
|
نيجني نوفغورود
| 15488
|
خيمكي
| 15296
|
تولا
| 15296
|
نوفوسيبيرسك
| 15264
|
تفير
| 15200
|
مياس
| 15104
|
إيفانوفو
| 15072
|
فولوغدا
| 15008
|
جوكوفسكي
| 14976
|
كوستروما
| 14912
|
سمارة
| 14880
|
كوروليف
| 14784
|
كالوغا
| 14720
|
تشيريبوفيتس
| 14720
|
سيفاستوبول
| 14688
|
بوشكينو
| 14528
|
ياروسلافل
| 14464
|
أوليانوفسك
| 14400
|
روستوف نا دون
| 14368
|
دوموديدوفو
| 14304
|
كامينسك-أورالسكي
| 14208
|
بسكوف
| 14144
|
يوشكار علا
| 14080
|
كيرتش
| 14080
|
مورمانسك
| 13920
|
توجلياتي
| 13920
|
فلاديمير
| 13792
|
النسر
| 13792
|
سيكتيفكار
| 13728
|
دولجوبرودني
| 13696
|
خانتي مانسيسك
| 13664
|
قازان
| 13600
|
إنجلز
| 13440
|
أرخانجيلسك
| 13280
|
بريانسك
| 13216
|
أومسك
| 13120
|
سيزران
| 13088
|
كراسنويارسك
| 13056
|
شيلكوفو
| 12928
|
بينزا
| 12864
|
تشيليابينسك
| 12768
|
تشيبوكساري
| 12768
|
نيجني تاجيل
| 12672
|
ستافروبول
| 12672
|
رامينسكوي
| 12640
|
ايركوتسك
| 12608
|
أنجارسك
| 12608
|
تيومين
| 12512
|
أودينتسوفو
| 12512
|
أوفا
| 12512
|
ماجادان
| 12512
|
بيرم
| 12448
|
كيروف
| 12256
|
نيجنكامسك
| 12224
|
مخاشكالا
| 12096
|
نيجنفارتوفسك
| 11936
|
كورسك
| 11904
|
سوتشي
| 11872
|
تامبوف
| 11840
|
بياتيغورسك
| 11808
|
فولجودونسك
| 11712
|
ريازان
| 11680
|
ساراتوف
| 11616
|
دزيرنسك
| 11456
|
أورينبورغ
| 11456
|
تل
| 11424
|
فولجوجراد
| 11264
|
إيجيفسك
| 11168
|
كريسوستوم
| 11136
|
ليبيتسك
| 11072
|
كيسلوفودسك
| 11072
|
سورجوت
| 11040
|
ماغنيتيوغورسك
| 10912
|
سمولينسك
| 10784
|
خاباروفسك
| 10752
|
كوبيسك
| 10688
|
مايكوب
| 10656
|
بيتروبافلوفسك كامتشاتسكي
| 10624
|
تاغونروغ
| 10560
|
بارناول
| 10528
|
سيرجيف بوساد
| 10368
|
إليستا
| 10304
|
سترليتاماك
| 9920
|
سيمفيروبول
| 9824
|
تومسك
| 9760
|
أوريخوفو زويفو
| 9728
|
استراخان
| 9664
|
يوباتوريا
| 9568
|
نوجينسك
| 9344
|
تشيتا
| 9216
|
بيلغورود
| 9120
|
Biysk
| 8928
|
ريبينسك
| 8896
|
سيفيرودفينسك
| 8832
|
فورونيج
| 8768
|
بلاغوفيشتشينسك
| 8672
|
نوفوروسيسك
| 8608
|
أولان أودي
| 8576
|
سيربوخوف
| 8320
|
كومسومولسك أون أمور
| 8192
|
أباكان
| 8128
|
نوريلسك
| 8096
|
يوجنو ساخالينسك
| 8032
|
أوبينسك
| 7904
|
إسنتوكي
| 7712
|
باتايسك
| 7648
|
فولجسكي
| 7584
|
نوفوتشركاسك
| 7488
|
بيردسك
| 7456
|
أرزاماس
| 7424
|
بيرفورالسك
| 7392
|
كيميروفو
| 7104
|
إلكتروستال
| 6720
|
ديربنت
| 6592
|
ياكوتسك
| 6528
|
موروم
| 6240
|
نفتيوجانسك
| 5792
|
ريوتوف
| 5696
|
بيروبيجان
| 5440
|
نوفوكويبيشيفسك
| 5248
|
سالخارد
| 5184
|
نوفوكوزنيتسك
| 5152
|
نوفي يورنغوي
| 4736
|
نويابرسك
| 4416
|
نوفوتشيبوكسارسك
| 4352
|
الصراصير
| 3968
|
كاسبييسك
| 3936
|
ستاري اوسكول
| 3840
|
أرتيوم
| 3744
|
زيليزنوغورسك
| 3584
|
Salavat
| 3584
|
بروكوبيفسك
| 2816
|
غورنو التايسك
| 2464
|
إعداد مجموعة بيانات للتدريب
وهكذا ، يتم تجميع مجموعة البيانات ، فكيف الآن ، وبعد الحصول على صورة لقسم الطريق والأشياء المجاورة ، اكتشف نوعية الإسفلت المبين عليها؟ قررت قطع جزء من الصورة بقياس 350 * 244 بكسل في وسط الصورة الأصلية أسفل الوسط مباشرة. ثم قم بتقليل القطعة المقطوعة أفقياً إلى حجم 244 بكسل. ستكون الصورة الناتجة (244 * 244 في الحجم) هي مدخلات المشفر التلافيفي:

من أجل فهم أفضل للبيانات التي أتعامل معها ، أول 2000 صورة قمت بتمييزها بنفسي ، تم تمييز بقية الصور بواسطة موظفي Yandex.Tolki. أمامهم طرحت سؤالا في الصياغة التالية.
أشر إلى سطح الطريق الذي تراه في الصورة:
- التربة / الأنقاض
- رصف الحجارة والبلاط والرصيف
- القضبان ، مسارات السكك الحديدية
- المياه ، برك كبيرة
- الأسفلت
- لا يوجد طريق في الصورة / أشياء غريبة / التغطية غير مرئية بسبب السيارات
إذا اختار المؤدي "Asphalt" ، فقد ظهرت قائمة تقدم لتقييم جودتها:
- تغطية ممتازة
- طفيف الشقوق واحد / الحفر واحد الضحلة
- الشقوق الكبيرة / الشقوق الشبكة / الحفر الصغيرة
- الحفر الكبيرة / الحفر العميقة / الطلاء المدمر
كما أظهر اختبار المهام ، فإن فناني Y. Toloki لا يختلفون في سلامة العمل - فهم ينقرون عن طريق الخطأ على الحقول باستخدام الماوس وينظرون في المهمة المكتملة. اضطررت إلى إضافة أسئلة تحكم (في المهمة كانت هناك 46 صورة ، منها 12 كانت مراقبة) وتمكين التأخير في القبول. بصفتي أسئلة تحكم ، استخدمت تلك الصور التي حددتها بنفسي. قمت تلقائيًا بالتأخير في القبول - Y. Toloka يسمح لك بتحميل نتائج العمل إلى ملف CSV ، وتحميل نتائج التحقق من الردود. تم التحقق من الإجابات على النحو التالي - إذا كانت المهمة تحتوي على أكثر من 5٪ إجابات غير صحيحة للتحكم في الأسئلة ، فسيتم اعتبارها غير مستوفاة. علاوة على ذلك ، إذا أشار المقاول إلى إجابة قريبة منطقياً من true ، فإن إجابته تعتبر صحيحة.
نتيجةً لذلك ، حصلت على حوالي 30 ألف صورة تحمل علامات ، والتي قررت توزيعها في ثلاثة فصول للتدريب:
- "جيد" - الصور المصنفة "الإسفلت: طلاء ممتاز" و "الأسفلت: الشقوق المفردة الصغيرة"
- "الأوسط" - صور تحمل علامات "حجارة الرصف ، والبلاط ، والرصيف" ، و "القضبان ، ومسارات السكك الحديدية" و "الأسفلت: شقوق كبيرة / شقوق الشبكة / الحفر الصغيرة المفردة"
- "كبير" - الصور التي تحمل علامات "التربة / الأحجار المكسرة" و "المياه والبرك الكبيرة" و "الأسفلت: عدد كبير من الحفر / الحفر العميقة / الرصيف المدمر"
- الصور الموسومة "لا يوجد طريق في الصورة / الأجسام الغريبة / التغطية غير مرئية بسبب السيارات" كان هناك عدد قليل جدًا (22 قطعة). لقد استثنيتهم من العمل الإضافي
تطوير المصنف والتدريب
لذلك ، يتم جمع البيانات وتصنيفها ، نواصل تطوير المصنف. عادة ، بالنسبة لمهام تصنيف الصور ، خاصة عند التدريب على مجموعات البيانات الصغيرة ، يتم استخدام برنامج تشفير تلافيفي جاهز ، لإخراج مصنف جديد متصل به. قررت استخدام مصنف بسيط بدون طبقة مخفية ، وطبقة إدخال بحجم 128 وطبقة إخراج بحجم 3. قررت استخدام خيارات متعددة جاهزة على الفور تم تدريبها على ImageNet كمشفرات:
- Xception
- Resnet
- التأسيس
- Vgg16
- Densenet121
- Mobilenet
فيما يلي الوظيفة التي تنشئ نموذج Keras باستخدام المشفر المحدد:
def createModel(typeModel): conv_base = None if(typeModel == "nasnet"): conv_base = keras.applications.nasnet.NASNetMobile(include_top=False, input_shape=(224,224,3), weights='imagenet') if(typeModel == "xception"): conv_base = keras.applications.xception.Xception(include_top=False, input_shape=(224,224,3), weights='imagenet') if(typeModel == "resnet"): conv_base = keras.applications.resnet50.ResNet50(include_top=False, input_shape=(224,224,3), weights='imagenet') if(typeModel == "inception"): conv_base = keras.applications.inception_v3.InceptionV3(include_top=False, input_shape=(224,224,3), weights='imagenet') if(typeModel == "densenet121"): conv_base = keras.applications.densenet.DenseNet121(include_top=False, input_shape=(224,224,3), weights='imagenet') if(typeModel == "mobilenet"): conv_base = keras.applications.mobilenet_v2.MobileNetV2(include_top=False, input_shape=(224,224,3), weights='imagenet') if(typeModel == "vgg16"): conv_base = keras.applications.vgg16.VGG16(include_top=False, input_shape=(224,224,3), weights='imagenet') conv_base.trainable = False model = Sequential() model.add(conv_base) model.add(Flatten()) model.add(Dense(128, activation='relu', kernel_regularizer=regularizers.l2(0.0002))) model.add(Dropout(0.3)) model.add(Dense(3, activation='softmax')) model.compile(optimizer=keras.optimizers.Adam(lr=1e-4), loss='binary_crossentropy', metrics=['accuracy']) return model
للتدريب ، استخدمت مولدًا مع زيادة (نظرًا لأن إمكانيات التعزيز المدمجة في Keras بدت لي غير كافية ، ثم استخدمت مكتبة
Augmentor ):
- المنحدرات
- تشويه عشوائي
- يتحول
- مبادلة اللون
- التحولات
- تغيير التباين والسطوع
- مضيفا ضوضاء عشوائية
- المحاصيل
بعد التكبير ، تبدو الصور كما يلي:

رمز المولد:
def get_datagen(): train_dir='~/data/train_img' test_dir='~/data/test_img' testDataGen = ImageDataGenerator(rescale=1. / 255) train_generator = datagen.flow_from_directory( train_dir, target_size=img_size, batch_size=16, class_mode='categorical') p = Augmentor.Pipeline(train_dir) p.skew(probability=0.9) p.random_distortion(probability=0.9,grid_width=3,grid_height=3,magnitude=8) p.rotate(probability=0.9, max_left_rotation=5, max_right_rotation=5) p.random_color(probability=0.7, min_factor=0.8, max_factor=1) p.flip_left_right(probability=0.7) p.random_brightness(probability=0.7, min_factor=0.8, max_factor=1.2) p.random_contrast(probability=0.5, min_factor=0.9, max_factor=1) p.random_erasing(probability=1,rectangle_area=0.2) p.crop_by_size(probability=1, width=244, height=244, centre=True) train_generator = keras_generator(p,batch_size=16) test_generator = testDataGen.flow_from_directory( test_dir, target_size=img_size, batch_size=32, class_mode='categorical') return (train_generator, test_generator)
يُظهر الرمز أن التعزيز لا يستخدم لبيانات الاختبار.
بوجود مولد مُولِّف ، يمكنك البدء في تدريب النموذج ، وسوف نقوم بتنفيذه على مرحلتين: أولاً ، قم بتدريب المصنف الخاص بنا فقط ، ثم النموذج بالكامل بالكامل.
def evalModelstep1(typeModel): K.clear_session() gc.collect() model=createModel(typeModel) traiGen,testGen=getDatagen() model.fit_generator(generator=traiGen, epochs=4, steps_per_epoch=30000/16, validation_steps=len(testGen), validation_data=testGen, ) return model def evalModelstep2(model): early_stopping_callback = EarlyStopping(monitor='val_loss', patience=3) model.layers[0].trainable=True model.trainable=True model.compile(optimizer=keras.optimizers.Adam(lr=1e-5), loss='binary_crossentropy', metrics=['accuracy']) traiGen,testGen=getDatagen() model.fit_generator(generator=traiGen, epochs=25, steps_per_epoch=30000/16, validation_steps=len(testGen), validation_data=testGen, callbacks=[early_stopping_callback] ) return model def full_fit(): model_names=[ "xception", "resnet", "inception", "vgg16", "densenet121", "mobilenet" ] for model_name in model_names: print("#########################################") print("#########################################") print("#########################################") print(model_name) print("#########################################") print("#########################################") print("#########################################") model = evalModelstep1(model_name) model = evalModelstep2(model) model.save("~/data/models/model_new_"+str(model_name)+".h5")
استدعاء full_fit () وانتظر. نحن ننتظر لفترة طويلة.
نتيجة لذلك ، سيكون لدينا ستة نماذج مدربة ، وسوف نتحقق من دقة هذه النماذج في جزء منفصل من البيانات ذات العلامات ؛ تلقيت ما يلي:
اسم النموذج
| دقة ٪
|
Xception
| 87.3
|
Resnet
| 90.8
|
التأسيس
| 90.2
|
Vgg16
| 89.2
|
Densenet121
| 90.6
|
Mobilenet
| 86.5
|
بشكل عام ، ليس كثيرًا ، ولكن مع مثل هذه العينة التدريبية الصغيرة ، لا يمكن للمرء أن يتوقع المزيد. لزيادة الدقة بشكل طفيف ، قمت بدمج مخرجات النماذج عن طريق حساب المتوسط:
def create_meta_model(): model_names=[ "xception", "resnet", "inception", "vgg16", "densenet121", "mobilenet" ] model_input = Input(shape=(244,244,3)) submodels=[] i=0; for model_name in model_names: filename= "~/data/models/model_new_"+str(model_name)+".h5" submodel = keras.models.load_model(filename) submodel.name = model_name+"_"+str(i) i+=1 submodels.append(submodel(model_input)) out=average(submodels) model = Model(inputs = model_input,outputs=out) model.compile(optimizer=keras.optimizers.Adam(lr=1e-4), loss='binary_crossentropy', metrics=['accuracy']) return model
كانت الدقة الناتجة 91.3 ٪. في هذه النتيجة ، قررت التوقف.
باستخدام المصنف
أخيرا المصنف جاهز ويمكن وضعه موضع التنفيذ! أقوم بإعداد بيانات الإدخال وأدير المصنف - أكثر من يوم بقليل وتم معالجة 1.7 مليون صورة. الآن الجزء الممتع هو النتائج. قم على الفور بإحضار أول عشر مدن في آخر عدد من الطرق مع تغطية جيدة:

الجدول الكامل (صورة قابلة للنقر) وهنا هو تصنيف جودة الطريق من قبل الموضوعات الفيدرالية:

التصنيف حسب المناطق الفيدرالية:

توزيع جودة الطرق في روسيا ككل:

حسنًا ، هذا كل شيء ، يمكن للجميع استخلاص النتائج بنفسه.
أخيرًا ، سأقدم أفضل الصور في كل فئة (والتي حصلت على الحد الأقصى للقيمة في فئتها):
ملاحظة: في التعليقات ، أشار بحق إلى عدم وجود إحصاءات عن سنوات استلام الصور. أصحح وأعطي طاولة:
سنة
| عدد الصور ، أجهزة الكمبيوتر
|
2008 | 37 |
2009 | 13 |
2010 | 157030 |
2011 | 60724 |
2012 | 42387 |
2013 | 12148
|
2014 | 141021
|
2015 | 46143
|
2016 | 410385
|
2017 | 324279
|
2018 | 581961
|