Dela via


Skapa en egen bildklassificerare med hjälp av Överför Learning

Innehållsförteckning

Sammanfattning

imageimageimageimage

Ovanstående bilder är testbilder som används i den andra delen av den här självstudien. Uppgiften är att träna en klassificerare som kan skilja mellan olika kategorier av bilder (i vårt exempel får och varg) genom att ändra en befintlig klassificerarmodell, basmodellen. Här använder vi en ResNet_18 modell som har tränats på ImageNet-corpus. Vi tränar bara på 15 bilder per klass på några sekunder och förutsäger alla 10 testbilder korrekt (observera de få saltkornen).

Följande är huvudresurserna för självstudien om överföringsutbildning:

Recept TransferLearning.py och TransferLearning_Extended.py (se Exempel/Bild/TransferLearning).
förtränade modeller Som basmodell för överföringsinlärning använder vi en förtränad ResNet_18 modell.
Data Datauppsättningen Flowers med 102 kategorier och exempelbilder på får och vargar (se Installation).
Så här kör du Följ beskrivningen nedan.

Installation

Om du vill köra koden i det här exemplet behöver du en CNTK Python miljö (se här för installationshjälp).

Om du vill ladda ned nödvändiga data och den förträngda modellen kör du följande kommando från mappen Examples/Image/TransferLearning :

python install_data_and_model.py

Kör exemplet

imageimageimageimage

I det här avsnittet skapar vi en klassificerare för datauppsättningen Flowers. Datauppsättningen skapades av Visual Geometry Group vid University of Oxford för bildklassificeringsuppgifter. Den består av 102 olika kategorier av blommor som är gemensamma för Storbritannien och innehåller ungefär 8 000 bilder som är uppdelade i tre uppsättningar av en gång 6000 och två gånger 1000 bilder. Mer information finns på VGG-startsidan.

Träna och utvärdera en överföringsinlärningsmodell i datauppsättningen Flowers

python TransferLearning.py

Modellen uppnår 93 % noggrannhet på datauppsättningen Flowers efter träning för 20 epoker.

Det grundläggande begreppet överföringsinlärning

När vi använder en basmodell för överföringsinlärning bygger vi i princip på de funktioner och koncept som lärdes under träningen av basmodellen. För en convolutional DNN, ResNet_18 i vårt fall, innebär det till exempel att vi skär av det slutliga kompakta lagret som ansvarar för att förutsäga klassetiketterna för den ursprungliga basmodellen och ersätta den med ett nytt kompakt lager som förutsäger klassetiketterna för vår nya uppgift. Indata till det gamla och det nya förutsägelselagret är desamma. Vi återanvänder helt enkelt de tränade funktionerna. Sedan tränar vi det här ändrade nätverket, antingen bara de nya vikterna för det nya förutsägelseskiktet eller alla vikter i hela nätverket.

Följande kod är den del av TransferLearning.py som skapar den nya modellen från basmodellen:

    # Load the pretrained classification net and find nodes
    base_model   = load_model(base_model_file)
    feature_node = find_by_name(base_model, feature_node_name)
    last_node    = find_by_name(base_model, last_hidden_node_name)

    # Clone the desired layers with fixed weights
    cloned_layers = combine([last_node.owner]).clone(
        CloneMethod.freeze if freeze else CloneMethod.clone,
        {feature_node: Placeholder(name='features')})

    # Add new dense layer for class prediction
    feat_norm  = input_features - Constant(114)
    cloned_out = cloned_layers(feat_norm)
    z          = Dense(num_classes, activation=None, name=new_output_node_name) (cloned_out)

Skapa en egen anpassad bildklassificerare

I föregående avsnitt tränade vi en klassificerare som skiljer 102 olika blomkategorier med ungefär 6 000 bilder för träning. I det här avsnittet använder vi bara 15 bilder per kategori för att skapa en klassificerare som kan skilja en varg från ett får. Vi använder samma ResNet_18 basmodell för överföringsinlärning. Träna och utvärdera modellkörningen

python TransferLearning_Extended.py

Modellen testas på fem bilder på var och en av fåren och vargen och förutsäger alla etiketter korrekt. Utdatafilen innehåller en JSON-representation per rad av förutsägelseresultatet:

[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":0.997, "Wolf":0.003}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "unknown", "predictions": {"Sheep":0.994, "Wolf":0.006}, "image": "..."}]
[{"class": "unknown", "predictions": {"Sheep":0.614, "Wolf":0.386}, "image": "..."}]
[{"class": "unknown", "predictions": {"Wolf":0.980, "Sheep":0.020}, "image": "..."}]

Observera att de tre sista bilderna inte har en tilldelad grundsanningsklass, vilket naturligtvis är ett giltigt scenario, t.ex. för bedömning av osedda bilder i en webbtjänst. De faktiska bilderna är de tre fågelbilderna som visas nedan. Grundsanningsklassen för dessa i JSON-utdata är inställd på unknown. Observera att förutsägelserna för de begrepp som klassificeraren har tränats på är ganska bra trots de få träningsbilderna. Detta är i stora delar på grund av den förtränad basmodellen. Förutsägelserna för osynliga begrepp, t.ex. bilder av fåglar, är naturligtvis inte särskilt meningsfulla, eftersom klassificeraren bara känner till får och varg. Mer om detta senare.

imageimageimage

Mappstruktur för anpassade bilduppsättningar

Du kan använda skriptet TransferLearning_Extended.py med dina egna avbildningar. Här är vad du behöver:

  1. class_mapping – En matris som innehåller namnen på dina kategorier, t.ex. ['wolf', 'sheep']
  2. train_map_file - En textfil som innehåller en bild-URL och en flik per rad avgränsade först motsvarande kategoriindex, t.ex. 0 för varg eller 1 får:
  3. test_map_file - En textfil som mappar testbilderna till motsvarande kategori. För okända kategorier i testbilder används -1 som kategoriindex.

Skriptet kan generera alla tre objekten ovan om du strukturerar bilderna på följande sätt:

<image root folder>
    Train
        Sheep
        Wolf
    Test
        Sheep
        Wolf
        <optional: image files with unknown label directly here>

Se <cntk root>/Examples/Image/DataSets/Animals/ som exempel. Varje undermapp i Train mappen betraktas som en kategori (endast på en nivå, ingen rekursion). För träning av enskilda bilder i rotmappen Train ignoreras eftersom de inte har någon tilldelad kategori. Ytterligare undermappar i Test mappen som inte förekommer i Train mappen ignoreras. För testning av enskilda okategoriserade bilder används även för bedömning, dvs. bilder som lagras direkt i Test mappen som de tre fågelbilderna i vårt exempel.

Om du vill använda dina anpassade avbildningsmappar måste du ange train_image_folder och test_image_folder överst i skriptet TransferLearning_Extended.py :

# define data location and characteristics
train_image_folder = "<your_image_root_folder>/Train"
test_image_folder = "<your_image_root_folder>/Test"

Kör sedan bara python TransferLearning_Extended.py. Så här använder du en annan basmodell.

För bedömning behöver du inte använda en test_map_file, t.ex. om du vill poängsätta enskilda bilder en i taget. Läs bara in den tränade överföringsinlärningsmodellen en gång och anropa eval_single_image sedan när du vill få förutsägelser för en ny bild:

    # once:
    # load the trained transfer learning model
    trained_model = load_model(new_model_file)

    # for every new image:
    # get predictions for a single image
    probs = eval_single_image(trained_model, img_file, image_width, image_height)

Några saltkorn

Dina träningsbilder bör täcka de scenarier som du vill poängsätta senare. Om klassificeraren ser helt nya begrepp eller kontexter kommer den troligen att fungera dåligt. Bara några exempel:

  • Du tränar bara på bilder från en begränsningsmiljö (till exempel inomhus) och försöker poängsätta bilder från en annan miljö (utomhus).
  • Du tränar bara på bilder av ett visst märke och försöker poängsätta andra.
  • Dina testbilder har i stort sett olika egenskaper, t.ex. när det gäller belysning, bakgrund, färg, storlek, position osv.
  • Dina testbilder innehåller helt nya begrepp.

Att lägga till en catch-all-kategori kan vara en bra idé, men bara om träningsdata för den kategorin innehåller bilder som återigen är tillräckligt lika de bilder du förväntar dig vid bedömningstiden. Som i exemplet ovan, om vi tränar en klassificerare med bilder av får och varg och använder den för att få en bild av en fågel, kan klassificeraren fortfarande bara tilldela en får- eller vargetikett, eftersom den inte känner till några andra kategorier. Om vi skulle lägga till en catch-all-kategori och lägga till träningsbilder av fåglar i den kan klassificeraren förutsäga klassen korrekt för fågelbilden. Men om vi presenterar den, t.ex. en bild av en bil, står den inför samma problem som tidigare eftersom den bara känner till får, varg och fågel (som vi bara råkade kalla catch-all). Därför måste dina träningsdata, även för catch-all, tillräckligt täcka de begrepp och bilder som du förväntar dig senare vid bedömningstiden.

En annan aspekt att tänka på är att en viss basmodell kan fungera mycket bra för vissa överföringsinlärningsuppgifter och inte lika bra för andra. Till exempel tränades ovanstående ResNet_18 modell på ImageNet-corpus, som innehåller många bilder av djur, människor, bilar och många andra objekt varje dag. Att använda den här basmodellen i överföringsinlärning för att skapa en klassificerare för liknande varje dag-objekt kan fungera bra. Om du använder samma modell som en basmodell för att skapa en klassificerare för bilder av mikroorganismer eller pennritningar kan det bara ge mediokra resultat.

Använda en annan basmodell

Om du vill använda en annan modell som basmodell måste du anpassa följande parametrar i TransferLearning.py (samma för TransferLearning_Extended.py):

# define base model location and characteristics
_base_model_file = os.path.join(base_folder, "..", "..", "..", "PretrainedModels", "ResNet_18.model")
_feature_node_name = "features"
_last_hidden_node_name = "z.x"
_image_height = 224
_image_width = 224
_num_channels = 3

För att undersöka vilka nodnamn som finns i din modell och vilka som ska väljas eftersom last_hidden_node du kan skriva ut alla nodnamn och nodformer med hjälp av följande rader (se __main__ metoden i TransferLearning.py):

    # You can use the following to inspect the base model and determine the desired node names
    node_outputs = get_node_outputs(load_model(_base_model_file))
    for out in node_outputs: print("{0} {1}".format(out.name, out.shape))