diff --git a/.jenkins/build.sh b/.jenkins/build.sh index eaed1d00057..df59585eed5 100755 --- a/.jenkins/build.sh +++ b/.jenkins/build.sh @@ -15,14 +15,22 @@ export PATH=/opt/conda/bin:$PATH rm -rf src pip install -r $DIR/../requirements.txt +# For Tensorboard. Until 1.14 moves to the release channel. +pip install tb-nightly + export PATH=/opt/conda/bin:$PATH -conda install -y sphinx==1.8.2 pandas +pip install sphinx==1.8.2 pandas + +# install awscli +# pip uninstall awscli +# pip install awscli==1.16.35 + # PyTorch Theme rm -rf src pip install -e git+git://github.com/pytorch/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme # pillow >= 4.2 will throw error when trying to write mode RGBA as JPEG, # this is a workaround to the issue. -pip install sphinx-gallery tqdm matplotlib ipython pillow==4.1.1 +pip install sphinx-gallery==0.3.1 tqdm matplotlib ipython pillow==4.1.1 # Install torchaudio from source git clone https://github.com/pytorch/audio --quiet @@ -30,15 +38,21 @@ pushd audio python setup.py install popd +# Install torchaudio from source +git clone https://github.com/pytorch/text --quiet +pushd text +python setup.py install +popd + aws configure set default.s3.multipart_threshold 5120MB # Decide whether to parallelize tutorial builds, based on $JOB_BASE_NAME export NUM_WORKERS=20 if [[ "${JOB_BASE_NAME}" == *worker_* ]]; then # Step 1: Remove runnable code from tutorials that are not supposed to be run - python $DIR/remove_runnable_code.py beginner_source/aws_distributed_training_tutorial.py beginner_source/aws_distributed_training_tutorial.py + python $DIR/remove_runnable_code.py beginner_source/aws_distributed_training_tutorial.py beginner_source/aws_distributed_training_tutorial.py || true # TODO: Fix bugs in these tutorials to make them runnable again - python $DIR/remove_runnable_code.py beginner_source/audio_classifier_tutorial.py beginner_source/audio_classifier_tutorial.py + python $DIR/remove_runnable_code.py beginner_source/audio_classifier_tutorial.py beginner_source/audio_classifier_tutorial.py || true # Step 2: Keep certain tutorials based on file count, and remove runnable code in all other tutorials # IMPORTANT NOTE: We assume that each tutorial has a UNIQUE filename. diff --git a/_static/imagenet_class_index.json b/_static/imagenet_class_index.json new file mode 100644 index 00000000000..5fe0dfefcd3 --- /dev/null +++ b/_static/imagenet_class_index.json @@ -0,0 +1 @@ +{"0": ["n01440764", "tench"], "1": ["n01443537", "goldfish"], "2": ["n01484850", "great_white_shark"], "3": ["n01491361", "tiger_shark"], "4": ["n01494475", "hammerhead"], "5": ["n01496331", "electric_ray"], "6": ["n01498041", "stingray"], "7": ["n01514668", "cock"], "8": ["n01514859", "hen"], "9": ["n01518878", "ostrich"], "10": ["n01530575", "brambling"], "11": ["n01531178", "goldfinch"], "12": ["n01532829", "house_finch"], "13": ["n01534433", "junco"], "14": ["n01537544", "indigo_bunting"], "15": ["n01558993", "robin"], "16": ["n01560419", "bulbul"], "17": ["n01580077", "jay"], "18": ["n01582220", "magpie"], "19": ["n01592084", "chickadee"], "20": ["n01601694", "water_ouzel"], "21": ["n01608432", "kite"], "22": ["n01614925", "bald_eagle"], "23": ["n01616318", "vulture"], "24": ["n01622779", "great_grey_owl"], "25": ["n01629819", "European_fire_salamander"], "26": ["n01630670", "common_newt"], "27": ["n01631663", "eft"], "28": ["n01632458", "spotted_salamander"], "29": ["n01632777", "axolotl"], "30": ["n01641577", "bullfrog"], "31": ["n01644373", "tree_frog"], "32": ["n01644900", "tailed_frog"], "33": ["n01664065", "loggerhead"], "34": ["n01665541", "leatherback_turtle"], "35": ["n01667114", "mud_turtle"], "36": ["n01667778", "terrapin"], "37": ["n01669191", "box_turtle"], "38": ["n01675722", "banded_gecko"], "39": ["n01677366", "common_iguana"], "40": ["n01682714", "American_chameleon"], "41": ["n01685808", "whiptail"], "42": ["n01687978", "agama"], "43": ["n01688243", "frilled_lizard"], "44": ["n01689811", "alligator_lizard"], "45": ["n01692333", "Gila_monster"], "46": ["n01693334", "green_lizard"], "47": ["n01694178", "African_chameleon"], "48": ["n01695060", "Komodo_dragon"], "49": ["n01697457", "African_crocodile"], "50": ["n01698640", "American_alligator"], "51": ["n01704323", "triceratops"], "52": ["n01728572", "thunder_snake"], "53": ["n01728920", "ringneck_snake"], "54": ["n01729322", "hognose_snake"], "55": ["n01729977", "green_snake"], "56": ["n01734418", "king_snake"], "57": ["n01735189", "garter_snake"], "58": ["n01737021", "water_snake"], "59": ["n01739381", "vine_snake"], "60": ["n01740131", "night_snake"], "61": ["n01742172", "boa_constrictor"], "62": ["n01744401", "rock_python"], "63": ["n01748264", "Indian_cobra"], "64": ["n01749939", "green_mamba"], "65": ["n01751748", "sea_snake"], "66": ["n01753488", "horned_viper"], "67": ["n01755581", "diamondback"], "68": ["n01756291", "sidewinder"], "69": ["n01768244", "trilobite"], "70": ["n01770081", "harvestman"], "71": ["n01770393", "scorpion"], "72": ["n01773157", "black_and_gold_garden_spider"], "73": ["n01773549", "barn_spider"], "74": ["n01773797", "garden_spider"], "75": ["n01774384", "black_widow"], "76": ["n01774750", "tarantula"], "77": ["n01775062", "wolf_spider"], "78": ["n01776313", "tick"], "79": ["n01784675", "centipede"], "80": ["n01795545", "black_grouse"], "81": ["n01796340", "ptarmigan"], "82": ["n01797886", "ruffed_grouse"], "83": ["n01798484", "prairie_chicken"], "84": ["n01806143", "peacock"], "85": ["n01806567", "quail"], "86": ["n01807496", "partridge"], "87": ["n01817953", "African_grey"], "88": ["n01818515", "macaw"], "89": ["n01819313", "sulphur-crested_cockatoo"], "90": ["n01820546", "lorikeet"], "91": ["n01824575", "coucal"], "92": ["n01828970", "bee_eater"], "93": ["n01829413", "hornbill"], "94": ["n01833805", "hummingbird"], "95": ["n01843065", "jacamar"], "96": ["n01843383", "toucan"], "97": ["n01847000", "drake"], "98": ["n01855032", "red-breasted_merganser"], "99": ["n01855672", "goose"], "100": ["n01860187", "black_swan"], "101": ["n01871265", "tusker"], "102": ["n01872401", "echidna"], "103": ["n01873310", "platypus"], "104": ["n01877812", "wallaby"], "105": ["n01882714", "koala"], "106": ["n01883070", "wombat"], "107": ["n01910747", "jellyfish"], "108": ["n01914609", "sea_anemone"], "109": ["n01917289", "brain_coral"], "110": ["n01924916", "flatworm"], "111": ["n01930112", "nematode"], "112": ["n01943899", "conch"], "113": ["n01944390", "snail"], "114": ["n01945685", "slug"], "115": ["n01950731", "sea_slug"], "116": ["n01955084", "chiton"], "117": ["n01968897", "chambered_nautilus"], "118": ["n01978287", "Dungeness_crab"], "119": ["n01978455", "rock_crab"], "120": ["n01980166", "fiddler_crab"], "121": ["n01981276", "king_crab"], "122": ["n01983481", "American_lobster"], "123": ["n01984695", "spiny_lobster"], "124": ["n01985128", "crayfish"], "125": ["n01986214", "hermit_crab"], "126": ["n01990800", "isopod"], "127": ["n02002556", "white_stork"], "128": ["n02002724", "black_stork"], "129": ["n02006656", "spoonbill"], "130": ["n02007558", "flamingo"], "131": ["n02009229", "little_blue_heron"], "132": ["n02009912", "American_egret"], "133": ["n02011460", "bittern"], "134": ["n02012849", "crane"], "135": ["n02013706", "limpkin"], "136": ["n02017213", "European_gallinule"], "137": ["n02018207", "American_coot"], "138": ["n02018795", "bustard"], "139": ["n02025239", "ruddy_turnstone"], "140": ["n02027492", "red-backed_sandpiper"], "141": ["n02028035", "redshank"], "142": ["n02033041", "dowitcher"], "143": ["n02037110", "oystercatcher"], "144": ["n02051845", "pelican"], "145": ["n02056570", "king_penguin"], "146": ["n02058221", "albatross"], "147": ["n02066245", "grey_whale"], "148": ["n02071294", "killer_whale"], "149": ["n02074367", "dugong"], "150": ["n02077923", "sea_lion"], "151": ["n02085620", "Chihuahua"], "152": ["n02085782", "Japanese_spaniel"], "153": ["n02085936", "Maltese_dog"], "154": ["n02086079", "Pekinese"], "155": ["n02086240", "Shih-Tzu"], "156": ["n02086646", "Blenheim_spaniel"], "157": ["n02086910", "papillon"], "158": ["n02087046", "toy_terrier"], "159": ["n02087394", "Rhodesian_ridgeback"], "160": ["n02088094", "Afghan_hound"], "161": ["n02088238", "basset"], "162": ["n02088364", "beagle"], "163": ["n02088466", "bloodhound"], "164": ["n02088632", "bluetick"], "165": ["n02089078", "black-and-tan_coonhound"], "166": ["n02089867", "Walker_hound"], "167": ["n02089973", "English_foxhound"], "168": ["n02090379", "redbone"], "169": ["n02090622", "borzoi"], "170": ["n02090721", "Irish_wolfhound"], "171": ["n02091032", "Italian_greyhound"], "172": ["n02091134", "whippet"], "173": ["n02091244", "Ibizan_hound"], "174": ["n02091467", "Norwegian_elkhound"], "175": ["n02091635", "otterhound"], "176": ["n02091831", "Saluki"], "177": ["n02092002", "Scottish_deerhound"], "178": ["n02092339", "Weimaraner"], "179": ["n02093256", "Staffordshire_bullterrier"], "180": ["n02093428", "American_Staffordshire_terrier"], "181": ["n02093647", "Bedlington_terrier"], "182": ["n02093754", "Border_terrier"], "183": ["n02093859", "Kerry_blue_terrier"], "184": ["n02093991", "Irish_terrier"], "185": ["n02094114", "Norfolk_terrier"], "186": ["n02094258", "Norwich_terrier"], "187": ["n02094433", "Yorkshire_terrier"], "188": ["n02095314", "wire-haired_fox_terrier"], "189": ["n02095570", "Lakeland_terrier"], "190": ["n02095889", "Sealyham_terrier"], "191": ["n02096051", "Airedale"], "192": ["n02096177", "cairn"], "193": ["n02096294", "Australian_terrier"], "194": ["n02096437", "Dandie_Dinmont"], "195": ["n02096585", "Boston_bull"], "196": ["n02097047", "miniature_schnauzer"], "197": ["n02097130", "giant_schnauzer"], "198": ["n02097209", "standard_schnauzer"], "199": ["n02097298", "Scotch_terrier"], "200": ["n02097474", "Tibetan_terrier"], "201": ["n02097658", "silky_terrier"], "202": ["n02098105", "soft-coated_wheaten_terrier"], "203": ["n02098286", "West_Highland_white_terrier"], "204": ["n02098413", "Lhasa"], "205": ["n02099267", "flat-coated_retriever"], "206": ["n02099429", "curly-coated_retriever"], "207": ["n02099601", "golden_retriever"], "208": ["n02099712", "Labrador_retriever"], "209": ["n02099849", "Chesapeake_Bay_retriever"], "210": ["n02100236", "German_short-haired_pointer"], "211": ["n02100583", "vizsla"], "212": ["n02100735", "English_setter"], "213": ["n02100877", "Irish_setter"], "214": ["n02101006", "Gordon_setter"], "215": ["n02101388", "Brittany_spaniel"], "216": ["n02101556", "clumber"], "217": ["n02102040", "English_springer"], "218": ["n02102177", "Welsh_springer_spaniel"], "219": ["n02102318", "cocker_spaniel"], "220": ["n02102480", "Sussex_spaniel"], "221": ["n02102973", "Irish_water_spaniel"], "222": ["n02104029", "kuvasz"], "223": ["n02104365", "schipperke"], "224": ["n02105056", "groenendael"], "225": ["n02105162", "malinois"], "226": ["n02105251", "briard"], "227": ["n02105412", "kelpie"], "228": ["n02105505", "komondor"], "229": ["n02105641", "Old_English_sheepdog"], "230": ["n02105855", "Shetland_sheepdog"], "231": ["n02106030", "collie"], "232": ["n02106166", "Border_collie"], "233": ["n02106382", "Bouvier_des_Flandres"], "234": ["n02106550", "Rottweiler"], "235": ["n02106662", "German_shepherd"], "236": ["n02107142", "Doberman"], "237": ["n02107312", "miniature_pinscher"], "238": ["n02107574", "Greater_Swiss_Mountain_dog"], "239": ["n02107683", "Bernese_mountain_dog"], "240": ["n02107908", "Appenzeller"], "241": ["n02108000", "EntleBucher"], "242": ["n02108089", "boxer"], "243": ["n02108422", "bull_mastiff"], "244": ["n02108551", "Tibetan_mastiff"], "245": ["n02108915", "French_bulldog"], "246": ["n02109047", "Great_Dane"], "247": ["n02109525", "Saint_Bernard"], "248": ["n02109961", "Eskimo_dog"], "249": ["n02110063", "malamute"], "250": ["n02110185", "Siberian_husky"], "251": ["n02110341", "dalmatian"], "252": ["n02110627", "affenpinscher"], "253": ["n02110806", "basenji"], "254": ["n02110958", "pug"], "255": ["n02111129", "Leonberg"], "256": ["n02111277", "Newfoundland"], "257": ["n02111500", "Great_Pyrenees"], "258": ["n02111889", "Samoyed"], "259": ["n02112018", "Pomeranian"], "260": ["n02112137", "chow"], "261": ["n02112350", "keeshond"], "262": ["n02112706", "Brabancon_griffon"], "263": ["n02113023", "Pembroke"], "264": ["n02113186", "Cardigan"], "265": ["n02113624", "toy_poodle"], "266": ["n02113712", "miniature_poodle"], "267": ["n02113799", "standard_poodle"], "268": ["n02113978", "Mexican_hairless"], "269": ["n02114367", "timber_wolf"], "270": ["n02114548", "white_wolf"], "271": ["n02114712", "red_wolf"], "272": ["n02114855", "coyote"], "273": ["n02115641", "dingo"], "274": ["n02115913", "dhole"], "275": ["n02116738", "African_hunting_dog"], "276": ["n02117135", "hyena"], "277": ["n02119022", "red_fox"], "278": ["n02119789", "kit_fox"], "279": ["n02120079", "Arctic_fox"], "280": ["n02120505", "grey_fox"], "281": ["n02123045", "tabby"], "282": ["n02123159", "tiger_cat"], "283": ["n02123394", "Persian_cat"], "284": ["n02123597", "Siamese_cat"], "285": ["n02124075", "Egyptian_cat"], "286": ["n02125311", "cougar"], "287": ["n02127052", "lynx"], "288": ["n02128385", "leopard"], "289": ["n02128757", "snow_leopard"], "290": ["n02128925", "jaguar"], "291": ["n02129165", "lion"], "292": ["n02129604", "tiger"], "293": ["n02130308", "cheetah"], "294": ["n02132136", "brown_bear"], "295": ["n02133161", "American_black_bear"], "296": ["n02134084", "ice_bear"], "297": ["n02134418", "sloth_bear"], "298": ["n02137549", "mongoose"], "299": ["n02138441", "meerkat"], "300": ["n02165105", "tiger_beetle"], "301": ["n02165456", "ladybug"], "302": ["n02167151", "ground_beetle"], "303": ["n02168699", "long-horned_beetle"], "304": ["n02169497", "leaf_beetle"], "305": ["n02172182", "dung_beetle"], "306": ["n02174001", "rhinoceros_beetle"], "307": ["n02177972", "weevil"], "308": ["n02190166", "fly"], "309": ["n02206856", "bee"], "310": ["n02219486", "ant"], "311": ["n02226429", "grasshopper"], "312": ["n02229544", "cricket"], "313": ["n02231487", "walking_stick"], "314": ["n02233338", "cockroach"], "315": ["n02236044", "mantis"], "316": ["n02256656", "cicada"], "317": ["n02259212", "leafhopper"], "318": ["n02264363", "lacewing"], "319": ["n02268443", "dragonfly"], "320": ["n02268853", "damselfly"], "321": ["n02276258", "admiral"], "322": ["n02277742", "ringlet"], "323": ["n02279972", "monarch"], "324": ["n02280649", "cabbage_butterfly"], "325": ["n02281406", "sulphur_butterfly"], "326": ["n02281787", "lycaenid"], "327": ["n02317335", "starfish"], "328": ["n02319095", "sea_urchin"], "329": ["n02321529", "sea_cucumber"], "330": ["n02325366", "wood_rabbit"], "331": ["n02326432", "hare"], "332": ["n02328150", "Angora"], "333": ["n02342885", "hamster"], "334": ["n02346627", "porcupine"], "335": ["n02356798", "fox_squirrel"], "336": ["n02361337", "marmot"], "337": ["n02363005", "beaver"], "338": ["n02364673", "guinea_pig"], "339": ["n02389026", "sorrel"], "340": ["n02391049", "zebra"], "341": ["n02395406", "hog"], "342": ["n02396427", "wild_boar"], "343": ["n02397096", "warthog"], "344": ["n02398521", "hippopotamus"], "345": ["n02403003", "ox"], "346": ["n02408429", "water_buffalo"], "347": ["n02410509", "bison"], "348": ["n02412080", "ram"], "349": ["n02415577", "bighorn"], "350": ["n02417914", "ibex"], "351": ["n02422106", "hartebeest"], "352": ["n02422699", "impala"], "353": ["n02423022", "gazelle"], "354": ["n02437312", "Arabian_camel"], "355": ["n02437616", "llama"], "356": ["n02441942", "weasel"], "357": ["n02442845", "mink"], "358": ["n02443114", "polecat"], "359": ["n02443484", "black-footed_ferret"], "360": ["n02444819", "otter"], "361": ["n02445715", "skunk"], "362": ["n02447366", "badger"], "363": ["n02454379", "armadillo"], "364": ["n02457408", "three-toed_sloth"], "365": ["n02480495", "orangutan"], "366": ["n02480855", "gorilla"], "367": ["n02481823", "chimpanzee"], "368": ["n02483362", "gibbon"], "369": ["n02483708", "siamang"], "370": ["n02484975", "guenon"], "371": ["n02486261", "patas"], "372": ["n02486410", "baboon"], "373": ["n02487347", "macaque"], "374": ["n02488291", "langur"], "375": ["n02488702", "colobus"], "376": ["n02489166", "proboscis_monkey"], "377": ["n02490219", "marmoset"], "378": ["n02492035", "capuchin"], "379": ["n02492660", "howler_monkey"], "380": ["n02493509", "titi"], "381": ["n02493793", "spider_monkey"], "382": ["n02494079", "squirrel_monkey"], "383": ["n02497673", "Madagascar_cat"], "384": ["n02500267", "indri"], "385": ["n02504013", "Indian_elephant"], "386": ["n02504458", "African_elephant"], "387": ["n02509815", "lesser_panda"], "388": ["n02510455", "giant_panda"], "389": ["n02514041", "barracouta"], "390": ["n02526121", "eel"], "391": ["n02536864", "coho"], "392": ["n02606052", "rock_beauty"], "393": ["n02607072", "anemone_fish"], "394": ["n02640242", "sturgeon"], "395": ["n02641379", "gar"], "396": ["n02643566", "lionfish"], "397": ["n02655020", "puffer"], "398": ["n02666196", "abacus"], "399": ["n02667093", "abaya"], "400": ["n02669723", "academic_gown"], "401": ["n02672831", "accordion"], "402": ["n02676566", "acoustic_guitar"], "403": ["n02687172", "aircraft_carrier"], "404": ["n02690373", "airliner"], "405": ["n02692877", "airship"], "406": ["n02699494", "altar"], "407": ["n02701002", "ambulance"], "408": ["n02704792", "amphibian"], "409": ["n02708093", "analog_clock"], "410": ["n02727426", "apiary"], "411": ["n02730930", "apron"], "412": ["n02747177", "ashcan"], "413": ["n02749479", "assault_rifle"], "414": ["n02769748", "backpack"], "415": ["n02776631", "bakery"], "416": ["n02777292", "balance_beam"], "417": ["n02782093", "balloon"], "418": ["n02783161", "ballpoint"], "419": ["n02786058", "Band_Aid"], "420": ["n02787622", "banjo"], "421": ["n02788148", "bannister"], "422": ["n02790996", "barbell"], "423": ["n02791124", "barber_chair"], "424": ["n02791270", "barbershop"], "425": ["n02793495", "barn"], "426": ["n02794156", "barometer"], "427": ["n02795169", "barrel"], "428": ["n02797295", "barrow"], "429": ["n02799071", "baseball"], "430": ["n02802426", "basketball"], "431": ["n02804414", "bassinet"], "432": ["n02804610", "bassoon"], "433": ["n02807133", "bathing_cap"], "434": ["n02808304", "bath_towel"], "435": ["n02808440", "bathtub"], "436": ["n02814533", "beach_wagon"], "437": ["n02814860", "beacon"], "438": ["n02815834", "beaker"], "439": ["n02817516", "bearskin"], "440": ["n02823428", "beer_bottle"], "441": ["n02823750", "beer_glass"], "442": ["n02825657", "bell_cote"], "443": ["n02834397", "bib"], "444": ["n02835271", "bicycle-built-for-two"], "445": ["n02837789", "bikini"], "446": ["n02840245", "binder"], "447": ["n02841315", "binoculars"], "448": ["n02843684", "birdhouse"], "449": ["n02859443", "boathouse"], "450": ["n02860847", "bobsled"], "451": ["n02865351", "bolo_tie"], "452": ["n02869837", "bonnet"], "453": ["n02870880", "bookcase"], "454": ["n02871525", "bookshop"], "455": ["n02877765", "bottlecap"], "456": ["n02879718", "bow"], "457": ["n02883205", "bow_tie"], "458": ["n02892201", "brass"], "459": ["n02892767", "brassiere"], "460": ["n02894605", "breakwater"], "461": ["n02895154", "breastplate"], "462": ["n02906734", "broom"], "463": ["n02909870", "bucket"], "464": ["n02910353", "buckle"], "465": ["n02916936", "bulletproof_vest"], "466": ["n02917067", "bullet_train"], "467": ["n02927161", "butcher_shop"], "468": ["n02930766", "cab"], "469": ["n02939185", "caldron"], "470": ["n02948072", "candle"], "471": ["n02950826", "cannon"], "472": ["n02951358", "canoe"], "473": ["n02951585", "can_opener"], "474": ["n02963159", "cardigan"], "475": ["n02965783", "car_mirror"], "476": ["n02966193", "carousel"], "477": ["n02966687", "carpenter's_kit"], "478": ["n02971356", "carton"], "479": ["n02974003", "car_wheel"], "480": ["n02977058", "cash_machine"], "481": ["n02978881", "cassette"], "482": ["n02979186", "cassette_player"], "483": ["n02980441", "castle"], "484": ["n02981792", "catamaran"], "485": ["n02988304", "CD_player"], "486": ["n02992211", "cello"], "487": ["n02992529", "cellular_telephone"], "488": ["n02999410", "chain"], "489": ["n03000134", "chainlink_fence"], "490": ["n03000247", "chain_mail"], "491": ["n03000684", "chain_saw"], "492": ["n03014705", "chest"], "493": ["n03016953", "chiffonier"], "494": ["n03017168", "chime"], "495": ["n03018349", "china_cabinet"], "496": ["n03026506", "Christmas_stocking"], "497": ["n03028079", "church"], "498": ["n03032252", "cinema"], "499": ["n03041632", "cleaver"], "500": ["n03042490", "cliff_dwelling"], "501": ["n03045698", "cloak"], "502": ["n03047690", "clog"], "503": ["n03062245", "cocktail_shaker"], "504": ["n03063599", "coffee_mug"], "505": ["n03063689", "coffeepot"], "506": ["n03065424", "coil"], "507": ["n03075370", "combination_lock"], "508": ["n03085013", "computer_keyboard"], "509": ["n03089624", "confectionery"], "510": ["n03095699", "container_ship"], "511": ["n03100240", "convertible"], "512": ["n03109150", "corkscrew"], "513": ["n03110669", "cornet"], "514": ["n03124043", "cowboy_boot"], "515": ["n03124170", "cowboy_hat"], "516": ["n03125729", "cradle"], "517": ["n03126707", "crane"], "518": ["n03127747", "crash_helmet"], "519": ["n03127925", "crate"], "520": ["n03131574", "crib"], "521": ["n03133878", "Crock_Pot"], "522": ["n03134739", "croquet_ball"], "523": ["n03141823", "crutch"], "524": ["n03146219", "cuirass"], "525": ["n03160309", "dam"], "526": ["n03179701", "desk"], "527": ["n03180011", "desktop_computer"], "528": ["n03187595", "dial_telephone"], "529": ["n03188531", "diaper"], "530": ["n03196217", "digital_clock"], "531": ["n03197337", "digital_watch"], "532": ["n03201208", "dining_table"], "533": ["n03207743", "dishrag"], "534": ["n03207941", "dishwasher"], "535": ["n03208938", "disk_brake"], "536": ["n03216828", "dock"], "537": ["n03218198", "dogsled"], "538": ["n03220513", "dome"], "539": ["n03223299", "doormat"], "540": ["n03240683", "drilling_platform"], "541": ["n03249569", "drum"], "542": ["n03250847", "drumstick"], "543": ["n03255030", "dumbbell"], "544": ["n03259280", "Dutch_oven"], "545": ["n03271574", "electric_fan"], "546": ["n03272010", "electric_guitar"], "547": ["n03272562", "electric_locomotive"], "548": ["n03290653", "entertainment_center"], "549": ["n03291819", "envelope"], "550": ["n03297495", "espresso_maker"], "551": ["n03314780", "face_powder"], "552": ["n03325584", "feather_boa"], "553": ["n03337140", "file"], "554": ["n03344393", "fireboat"], "555": ["n03345487", "fire_engine"], "556": ["n03347037", "fire_screen"], "557": ["n03355925", "flagpole"], "558": ["n03372029", "flute"], "559": ["n03376595", "folding_chair"], "560": ["n03379051", "football_helmet"], "561": ["n03384352", "forklift"], "562": ["n03388043", "fountain"], "563": ["n03388183", "fountain_pen"], "564": ["n03388549", "four-poster"], "565": ["n03393912", "freight_car"], "566": ["n03394916", "French_horn"], "567": ["n03400231", "frying_pan"], "568": ["n03404251", "fur_coat"], "569": ["n03417042", "garbage_truck"], "570": ["n03424325", "gasmask"], "571": ["n03425413", "gas_pump"], "572": ["n03443371", "goblet"], "573": ["n03444034", "go-kart"], "574": ["n03445777", "golf_ball"], "575": ["n03445924", "golfcart"], "576": ["n03447447", "gondola"], "577": ["n03447721", "gong"], "578": ["n03450230", "gown"], "579": ["n03452741", "grand_piano"], "580": ["n03457902", "greenhouse"], "581": ["n03459775", "grille"], "582": ["n03461385", "grocery_store"], "583": ["n03467068", "guillotine"], "584": ["n03476684", "hair_slide"], "585": ["n03476991", "hair_spray"], "586": ["n03478589", "half_track"], "587": ["n03481172", "hammer"], "588": ["n03482405", "hamper"], "589": ["n03483316", "hand_blower"], "590": ["n03485407", "hand-held_computer"], "591": ["n03485794", "handkerchief"], "592": ["n03492542", "hard_disc"], "593": ["n03494278", "harmonica"], "594": ["n03495258", "harp"], "595": ["n03496892", "harvester"], "596": ["n03498962", "hatchet"], "597": ["n03527444", "holster"], "598": ["n03529860", "home_theater"], "599": ["n03530642", "honeycomb"], "600": ["n03532672", "hook"], "601": ["n03534580", "hoopskirt"], "602": ["n03535780", "horizontal_bar"], "603": ["n03538406", "horse_cart"], "604": ["n03544143", "hourglass"], "605": ["n03584254", "iPod"], "606": ["n03584829", "iron"], "607": ["n03590841", "jack-o'-lantern"], "608": ["n03594734", "jean"], "609": ["n03594945", "jeep"], "610": ["n03595614", "jersey"], "611": ["n03598930", "jigsaw_puzzle"], "612": ["n03599486", "jinrikisha"], "613": ["n03602883", "joystick"], "614": ["n03617480", "kimono"], "615": ["n03623198", "knee_pad"], "616": ["n03627232", "knot"], "617": ["n03630383", "lab_coat"], "618": ["n03633091", "ladle"], "619": ["n03637318", "lampshade"], "620": ["n03642806", "laptop"], "621": ["n03649909", "lawn_mower"], "622": ["n03657121", "lens_cap"], "623": ["n03658185", "letter_opener"], "624": ["n03661043", "library"], "625": ["n03662601", "lifeboat"], "626": ["n03666591", "lighter"], "627": ["n03670208", "limousine"], "628": ["n03673027", "liner"], "629": ["n03676483", "lipstick"], "630": ["n03680355", "Loafer"], "631": ["n03690938", "lotion"], "632": ["n03691459", "loudspeaker"], "633": ["n03692522", "loupe"], "634": ["n03697007", "lumbermill"], "635": ["n03706229", "magnetic_compass"], "636": ["n03709823", "mailbag"], "637": ["n03710193", "mailbox"], "638": ["n03710637", "maillot"], "639": ["n03710721", "maillot"], "640": ["n03717622", "manhole_cover"], "641": ["n03720891", "maraca"], "642": ["n03721384", "marimba"], "643": ["n03724870", "mask"], "644": ["n03729826", "matchstick"], "645": ["n03733131", "maypole"], "646": ["n03733281", "maze"], "647": ["n03733805", "measuring_cup"], "648": ["n03742115", "medicine_chest"], "649": ["n03743016", "megalith"], "650": ["n03759954", "microphone"], "651": ["n03761084", "microwave"], "652": ["n03763968", "military_uniform"], "653": ["n03764736", "milk_can"], "654": ["n03769881", "minibus"], "655": ["n03770439", "miniskirt"], "656": ["n03770679", "minivan"], "657": ["n03773504", "missile"], "658": ["n03775071", "mitten"], "659": ["n03775546", "mixing_bowl"], "660": ["n03776460", "mobile_home"], "661": ["n03777568", "Model_T"], "662": ["n03777754", "modem"], "663": ["n03781244", "monastery"], "664": ["n03782006", "monitor"], "665": ["n03785016", "moped"], "666": ["n03786901", "mortar"], "667": ["n03787032", "mortarboard"], "668": ["n03788195", "mosque"], "669": ["n03788365", "mosquito_net"], "670": ["n03791053", "motor_scooter"], "671": ["n03792782", "mountain_bike"], "672": ["n03792972", "mountain_tent"], "673": ["n03793489", "mouse"], "674": ["n03794056", "mousetrap"], "675": ["n03796401", "moving_van"], "676": ["n03803284", "muzzle"], "677": ["n03804744", "nail"], "678": ["n03814639", "neck_brace"], "679": ["n03814906", "necklace"], "680": ["n03825788", "nipple"], "681": ["n03832673", "notebook"], "682": ["n03837869", "obelisk"], "683": ["n03838899", "oboe"], "684": ["n03840681", "ocarina"], "685": ["n03841143", "odometer"], "686": ["n03843555", "oil_filter"], "687": ["n03854065", "organ"], "688": ["n03857828", "oscilloscope"], "689": ["n03866082", "overskirt"], "690": ["n03868242", "oxcart"], "691": ["n03868863", "oxygen_mask"], "692": ["n03871628", "packet"], "693": ["n03873416", "paddle"], "694": ["n03874293", "paddlewheel"], "695": ["n03874599", "padlock"], "696": ["n03876231", "paintbrush"], "697": ["n03877472", "pajama"], "698": ["n03877845", "palace"], "699": ["n03884397", "panpipe"], "700": ["n03887697", "paper_towel"], "701": ["n03888257", "parachute"], "702": ["n03888605", "parallel_bars"], "703": ["n03891251", "park_bench"], "704": ["n03891332", "parking_meter"], "705": ["n03895866", "passenger_car"], "706": ["n03899768", "patio"], "707": ["n03902125", "pay-phone"], "708": ["n03903868", "pedestal"], "709": ["n03908618", "pencil_box"], "710": ["n03908714", "pencil_sharpener"], "711": ["n03916031", "perfume"], "712": ["n03920288", "Petri_dish"], "713": ["n03924679", "photocopier"], "714": ["n03929660", "pick"], "715": ["n03929855", "pickelhaube"], "716": ["n03930313", "picket_fence"], "717": ["n03930630", "pickup"], "718": ["n03933933", "pier"], "719": ["n03935335", "piggy_bank"], "720": ["n03937543", "pill_bottle"], "721": ["n03938244", "pillow"], "722": ["n03942813", "ping-pong_ball"], "723": ["n03944341", "pinwheel"], "724": ["n03947888", "pirate"], "725": ["n03950228", "pitcher"], "726": ["n03954731", "plane"], "727": ["n03956157", "planetarium"], "728": ["n03958227", "plastic_bag"], "729": ["n03961711", "plate_rack"], "730": ["n03967562", "plow"], "731": ["n03970156", "plunger"], "732": ["n03976467", "Polaroid_camera"], "733": ["n03976657", "pole"], "734": ["n03977966", "police_van"], "735": ["n03980874", "poncho"], "736": ["n03982430", "pool_table"], "737": ["n03983396", "pop_bottle"], "738": ["n03991062", "pot"], "739": ["n03992509", "potter's_wheel"], "740": ["n03995372", "power_drill"], "741": ["n03998194", "prayer_rug"], "742": ["n04004767", "printer"], "743": ["n04005630", "prison"], "744": ["n04008634", "projectile"], "745": ["n04009552", "projector"], "746": ["n04019541", "puck"], "747": ["n04023962", "punching_bag"], "748": ["n04026417", "purse"], "749": ["n04033901", "quill"], "750": ["n04033995", "quilt"], "751": ["n04037443", "racer"], "752": ["n04039381", "racket"], "753": ["n04040759", "radiator"], "754": ["n04041544", "radio"], "755": ["n04044716", "radio_telescope"], "756": ["n04049303", "rain_barrel"], "757": ["n04065272", "recreational_vehicle"], "758": ["n04067472", "reel"], "759": ["n04069434", "reflex_camera"], "760": ["n04070727", "refrigerator"], "761": ["n04074963", "remote_control"], "762": ["n04081281", "restaurant"], "763": ["n04086273", "revolver"], "764": ["n04090263", "rifle"], "765": ["n04099969", "rocking_chair"], "766": ["n04111531", "rotisserie"], "767": ["n04116512", "rubber_eraser"], "768": ["n04118538", "rugby_ball"], "769": ["n04118776", "rule"], "770": ["n04120489", "running_shoe"], "771": ["n04125021", "safe"], "772": ["n04127249", "safety_pin"], "773": ["n04131690", "saltshaker"], "774": ["n04133789", "sandal"], "775": ["n04136333", "sarong"], "776": ["n04141076", "sax"], "777": ["n04141327", "scabbard"], "778": ["n04141975", "scale"], "779": ["n04146614", "school_bus"], "780": ["n04147183", "schooner"], "781": ["n04149813", "scoreboard"], "782": ["n04152593", "screen"], "783": ["n04153751", "screw"], "784": ["n04154565", "screwdriver"], "785": ["n04162706", "seat_belt"], "786": ["n04179913", "sewing_machine"], "787": ["n04192698", "shield"], "788": ["n04200800", "shoe_shop"], "789": ["n04201297", "shoji"], "790": ["n04204238", "shopping_basket"], "791": ["n04204347", "shopping_cart"], "792": ["n04208210", "shovel"], "793": ["n04209133", "shower_cap"], "794": ["n04209239", "shower_curtain"], "795": ["n04228054", "ski"], "796": ["n04229816", "ski_mask"], "797": ["n04235860", "sleeping_bag"], "798": ["n04238763", "slide_rule"], "799": ["n04239074", "sliding_door"], "800": ["n04243546", "slot"], "801": ["n04251144", "snorkel"], "802": ["n04252077", "snowmobile"], "803": ["n04252225", "snowplow"], "804": ["n04254120", "soap_dispenser"], "805": ["n04254680", "soccer_ball"], "806": ["n04254777", "sock"], "807": ["n04258138", "solar_dish"], "808": ["n04259630", "sombrero"], "809": ["n04263257", "soup_bowl"], "810": ["n04264628", "space_bar"], "811": ["n04265275", "space_heater"], "812": ["n04266014", "space_shuttle"], "813": ["n04270147", "spatula"], "814": ["n04273569", "speedboat"], "815": ["n04275548", "spider_web"], "816": ["n04277352", "spindle"], "817": ["n04285008", "sports_car"], "818": ["n04286575", "spotlight"], "819": ["n04296562", "stage"], "820": ["n04310018", "steam_locomotive"], "821": ["n04311004", "steel_arch_bridge"], "822": ["n04311174", "steel_drum"], "823": ["n04317175", "stethoscope"], "824": ["n04325704", "stole"], "825": ["n04326547", "stone_wall"], "826": ["n04328186", "stopwatch"], "827": ["n04330267", "stove"], "828": ["n04332243", "strainer"], "829": ["n04335435", "streetcar"], "830": ["n04336792", "stretcher"], "831": ["n04344873", "studio_couch"], "832": ["n04346328", "stupa"], "833": ["n04347754", "submarine"], "834": ["n04350905", "suit"], "835": ["n04355338", "sundial"], "836": ["n04355933", "sunglass"], "837": ["n04356056", "sunglasses"], "838": ["n04357314", "sunscreen"], "839": ["n04366367", "suspension_bridge"], "840": ["n04367480", "swab"], "841": ["n04370456", "sweatshirt"], "842": ["n04371430", "swimming_trunks"], "843": ["n04371774", "swing"], "844": ["n04372370", "switch"], "845": ["n04376876", "syringe"], "846": ["n04380533", "table_lamp"], "847": ["n04389033", "tank"], "848": ["n04392985", "tape_player"], "849": ["n04398044", "teapot"], "850": ["n04399382", "teddy"], "851": ["n04404412", "television"], "852": ["n04409515", "tennis_ball"], "853": ["n04417672", "thatch"], "854": ["n04418357", "theater_curtain"], "855": ["n04423845", "thimble"], "856": ["n04428191", "thresher"], "857": ["n04429376", "throne"], "858": ["n04435653", "tile_roof"], "859": ["n04442312", "toaster"], "860": ["n04443257", "tobacco_shop"], "861": ["n04447861", "toilet_seat"], "862": ["n04456115", "torch"], "863": ["n04458633", "totem_pole"], "864": ["n04461696", "tow_truck"], "865": ["n04462240", "toyshop"], "866": ["n04465501", "tractor"], "867": ["n04467665", "trailer_truck"], "868": ["n04476259", "tray"], "869": ["n04479046", "trench_coat"], "870": ["n04482393", "tricycle"], "871": ["n04483307", "trimaran"], "872": ["n04485082", "tripod"], "873": ["n04486054", "triumphal_arch"], "874": ["n04487081", "trolleybus"], "875": ["n04487394", "trombone"], "876": ["n04493381", "tub"], "877": ["n04501370", "turnstile"], "878": ["n04505470", "typewriter_keyboard"], "879": ["n04507155", "umbrella"], "880": ["n04509417", "unicycle"], "881": ["n04515003", "upright"], "882": ["n04517823", "vacuum"], "883": ["n04522168", "vase"], "884": ["n04523525", "vault"], "885": ["n04525038", "velvet"], "886": ["n04525305", "vending_machine"], "887": ["n04532106", "vestment"], "888": ["n04532670", "viaduct"], "889": ["n04536866", "violin"], "890": ["n04540053", "volleyball"], "891": ["n04542943", "waffle_iron"], "892": ["n04548280", "wall_clock"], "893": ["n04548362", "wallet"], "894": ["n04550184", "wardrobe"], "895": ["n04552348", "warplane"], "896": ["n04553703", "washbasin"], "897": ["n04554684", "washer"], "898": ["n04557648", "water_bottle"], "899": ["n04560804", "water_jug"], "900": ["n04562935", "water_tower"], "901": ["n04579145", "whiskey_jug"], "902": ["n04579432", "whistle"], "903": ["n04584207", "wig"], "904": ["n04589890", "window_screen"], "905": ["n04590129", "window_shade"], "906": ["n04591157", "Windsor_tie"], "907": ["n04591713", "wine_bottle"], "908": ["n04592741", "wing"], "909": ["n04596742", "wok"], "910": ["n04597913", "wooden_spoon"], "911": ["n04599235", "wool"], "912": ["n04604644", "worm_fence"], "913": ["n04606251", "wreck"], "914": ["n04612504", "yawl"], "915": ["n04613696", "yurt"], "916": ["n06359193", "web_site"], "917": ["n06596364", "comic_book"], "918": ["n06785654", "crossword_puzzle"], "919": ["n06794110", "street_sign"], "920": ["n06874185", "traffic_light"], "921": ["n07248320", "book_jacket"], "922": ["n07565083", "menu"], "923": ["n07579787", "plate"], "924": ["n07583066", "guacamole"], "925": ["n07584110", "consomme"], "926": ["n07590611", "hot_pot"], "927": ["n07613480", "trifle"], "928": ["n07614500", "ice_cream"], "929": ["n07615774", "ice_lolly"], "930": ["n07684084", "French_loaf"], "931": ["n07693725", "bagel"], "932": ["n07695742", "pretzel"], "933": ["n07697313", "cheeseburger"], "934": ["n07697537", "hotdog"], "935": ["n07711569", "mashed_potato"], "936": ["n07714571", "head_cabbage"], "937": ["n07714990", "broccoli"], "938": ["n07715103", "cauliflower"], "939": ["n07716358", "zucchini"], "940": ["n07716906", "spaghetti_squash"], "941": ["n07717410", "acorn_squash"], "942": ["n07717556", "butternut_squash"], "943": ["n07718472", "cucumber"], "944": ["n07718747", "artichoke"], "945": ["n07720875", "bell_pepper"], "946": ["n07730033", "cardoon"], "947": ["n07734744", "mushroom"], "948": ["n07742313", "Granny_Smith"], "949": ["n07745940", "strawberry"], "950": ["n07747607", "orange"], "951": ["n07749582", "lemon"], "952": ["n07753113", "fig"], "953": ["n07753275", "pineapple"], "954": ["n07753592", "banana"], "955": ["n07754684", "jackfruit"], "956": ["n07760859", "custard_apple"], "957": ["n07768694", "pomegranate"], "958": ["n07802026", "hay"], "959": ["n07831146", "carbonara"], "960": ["n07836838", "chocolate_sauce"], "961": ["n07860988", "dough"], "962": ["n07871810", "meat_loaf"], "963": ["n07873807", "pizza"], "964": ["n07875152", "potpie"], "965": ["n07880968", "burrito"], "966": ["n07892512", "red_wine"], "967": ["n07920052", "espresso"], "968": ["n07930864", "cup"], "969": ["n07932039", "eggnog"], "970": ["n09193705", "alp"], "971": ["n09229709", "bubble"], "972": ["n09246464", "cliff"], "973": ["n09256479", "coral_reef"], "974": ["n09288635", "geyser"], "975": ["n09332890", "lakeside"], "976": ["n09399592", "promontory"], "977": ["n09421951", "sandbar"], "978": ["n09428293", "seashore"], "979": ["n09468604", "valley"], "980": ["n09472597", "volcano"], "981": ["n09835506", "ballplayer"], "982": ["n10148035", "groom"], "983": ["n10565667", "scuba_diver"], "984": ["n11879895", "rapeseed"], "985": ["n11939491", "daisy"], "986": ["n12057211", "yellow_lady's_slipper"], "987": ["n12144580", "corn"], "988": ["n12267677", "acorn"], "989": ["n12620546", "hip"], "990": ["n12768682", "buckeye"], "991": ["n12985857", "coral_fungus"], "992": ["n12998815", "agaric"], "993": ["n13037406", "gyromitra"], "994": ["n13040303", "stinkhorn"], "995": ["n13044778", "earthstar"], "996": ["n13052670", "hen-of-the-woods"], "997": ["n13054560", "bolete"], "998": ["n13133613", "ear"], "999": ["n15075141", "toilet_tissue"]} \ No newline at end of file diff --git a/_static/img/cat_output1.png b/_static/img/cat_output1.png deleted file mode 100755 index 92694433024..00000000000 Binary files a/_static/img/cat_output1.png and /dev/null differ diff --git a/_static/img/cat_superres_with_ort.jpg b/_static/img/cat_superres_with_ort.jpg new file mode 100644 index 00000000000..7e4143c3e79 Binary files /dev/null and b/_static/img/cat_superres_with_ort.jpg differ diff --git a/_static/img/chatbot/diff.png b/_static/img/chatbot/diff.png old mode 100755 new mode 100644 index 525c426679d..fc3cc56789b Binary files a/_static/img/chatbot/diff.png and b/_static/img/chatbot/diff.png differ diff --git a/_static/img/chatbot/pytorch_workflow.png b/_static/img/chatbot/pytorch_workflow.png old mode 100755 new mode 100644 index 1598490ddad..8a81f1995f0 Binary files a/_static/img/chatbot/pytorch_workflow.png and b/_static/img/chatbot/pytorch_workflow.png differ diff --git a/_static/img/flask.png b/_static/img/flask.png new file mode 100644 index 00000000000..bad6738efdd Binary files /dev/null and b/_static/img/flask.png differ diff --git a/_static/img/hybrid.png b/_static/img/hybrid.png deleted file mode 100755 index d4dc016d863..00000000000 Binary files a/_static/img/hybrid.png and /dev/null differ diff --git a/_static/img/sample_file.jpeg b/_static/img/sample_file.jpeg new file mode 100644 index 00000000000..a7b314bd969 Binary files /dev/null and b/_static/img/sample_file.jpeg differ diff --git a/_static/img/torchscript.png b/_static/img/torchscript.png new file mode 100644 index 00000000000..b748d45d231 Binary files /dev/null and b/_static/img/torchscript.png differ diff --git a/advanced_source/README.txt b/advanced_source/README.txt index 50dacb8c362..0dbaffef5f7 100644 --- a/advanced_source/README.txt +++ b/advanced_source/README.txt @@ -13,6 +13,6 @@ Advanced Tutorials Custom C Extensions for PyTorch https://pytorch.org/tutorials/advanced/c_extension.html -4. super_resolution_with_caffe2.py - Transfering a Model from PyTorch to Caffe2 and Mobile using ONNX - https://pytorch.org/tutorials/advanced/super_resolution_with_caffe2.html +4. super_resolution_with_onnxruntime.py + Exporting a Model from PyTorch to ONNX and Running it using ONNXRuntime + https://pytorch.org/tutorials/advanced/super_resolution_with_onnxruntime.html diff --git a/advanced_source/cpp_export.rst b/advanced_source/cpp_export.rst index e0081377584..068029ea6e2 100644 --- a/advanced_source/cpp_export.rst +++ b/advanced_source/cpp_export.rst @@ -92,28 +92,30 @@ vanilla Pytorch model:: Because the ``forward`` method of this module uses control flow that is dependent on the input, it is not suitable for tracing. Instead, we can convert -it to a ``ScriptModule`` by subclassing it from ``torch.jit.ScriptModule`` and -adding a ``@torch.jit.script_method`` annotation to the model's ``forward`` -method:: - - import torch - - class MyModule(torch.jit.ScriptModule): - def __init__(self, N, M): - super(MyModule, self).__init__() - self.weight = torch.nn.Parameter(torch.rand(N, M)) - - @torch.jit.script_method - def forward(self, input): - if bool(input.sum() > 0): - output = self.weight.mv(input) - else: - output = self.weight + input - return output - - my_script_module = MyModule(2, 3) - -Creating a new ``MyModule`` object now directly produces an instance of +it to a ``ScriptModule``. +In order to convert the module to the ``ScriptModule``, one needs to +compile the module with ``torch.jit.script`` as follows:: + + class MyModule(torch.nn.Module): + def __init__(self, N, M): + super(MyModule, self).__init__() + self.weight = torch.nn.Parameter(torch.rand(N, M)) + + def forward(self, input): + if input.sum() > 0: + output = self.weight.mv(input) + else: + output = self.weight + input + return output + + my_module = MyModule(10,20) + sm = torch.jit.script(my_module) + +If you need to exclude some methods in your ``nn.Module`` +because they use Python features that TorchScript doesn't support yet, +you could annotate those with ``@torch.jit.ignore`` + +``my_module`` is an instance of ``ScriptModule`` that is ready for serialization. Step 2: Serializing Your Script Module to a File @@ -152,32 +154,38 @@ do: .. code-block:: cpp - #include // One-stop header. - - #include - #include - - int main(int argc, const char* argv[]) { - if (argc != 2) { - std::cerr << "usage: example-app \n"; - return -1; + #include // One-stop header. + + #include + #include + + int main(int argc, const char* argv[]) { + if (argc != 2) { + std::cerr << "usage: example-app \n"; + return -1; + } + + + torch::jit::script::Module module; + try { + // Deserialize the ScriptModule from a file using torch::jit::load(). + module = torch::jit::load(argv[1]); + } + catch (const c10::Error& e) { + std::cerr << "error loading the model\n"; + return -1; + } + + std::cout << "ok\n"; } - // Deserialize the ScriptModule from a file using torch::jit::load(). - std::shared_ptr module = torch::jit::load(argv[1]); - - assert(module != nullptr); - std::cout << "ok\n"; - } The ```` header encompasses all relevant includes from the LibTorch library necessary to run the example. Our application accepts the file path to a serialized PyTorch ``ScriptModule`` as its only command line argument and then proceeds to deserialize the module using the ``torch::jit::load()`` -function, which takes this file path as input. In return we receive a shared -pointer to a ``torch::jit::script::Module``, the equivalent to a -``torch.jit.ScriptModule`` in C++. For now, we only verify that this pointer is -not null. We will examine how to execute it in a moment. +function, which takes this file path as input. In return we receive a ``torch::jit::script::Module`` +object. We will examine how to execute it in a moment. Depending on LibTorch and Building the Application ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -300,8 +308,7 @@ application's ``main()`` function: inputs.push_back(torch::ones({1, 3, 224, 224})); // Execute the model and turn its output into a tensor. - at::Tensor output = module->forward(inputs).toTensor(); - + at::Tensor output = module.forward(inputs).toTensor(); std::cout << output.slice(/*dim=*/1, /*start=*/0, /*end=*/5) << '\n'; The first two lines set up the inputs to our model. We create a vector of @@ -344,7 +351,7 @@ Looks like a good match! .. tip:: - To move your model to GPU memory, you can write ``model->to(at::kCUDA);``. + To move your model to GPU memory, you can write ``model.to(at::kCUDA);``. Make sure the inputs to a model living in CUDA memory are also in CUDA memory by calling ``tensor.to(at::kCUDA)``, which will return a new tensor in CUDA memory. diff --git a/advanced_source/cpp_extension.rst b/advanced_source/cpp_extension.rst index 383d14851d1..6db82bc7711 100644 --- a/advanced_source/cpp_extension.rst +++ b/advanced_source/cpp_extension.rst @@ -147,23 +147,22 @@ For the "ahead of time" flavor, we build our C++ extension by writing a ``setup.py`` script that uses setuptools to compile our C++ code. For the LLTM, it looks as simple as this:: - from setuptools import setup - from torch.utils.cpp_extension import CppExtension, BuildExtension + from setuptools import setup, Extension + from torch.utils import cpp_extension setup(name='lltm_cpp', - ext_modules=[CppExtension('lltm', ['lltm.cpp'])], - cmdclass={'build_ext': BuildExtension}) - + ext_modules=[cpp_extension.CppExtension('lltm_cpp', ['lltm.cpp'])], + cmdclass={'build_ext': cpp_extension.BuildExtension}) In this code, :class:`CppExtension` is a convenience wrapper around :class:`setuptools.Extension` that passes the correct include paths and sets the language of the extension to C++. The equivalent vanilla :mod:`setuptools` code would simply be:: - setuptools.Extension( + Extension( name='lltm_cpp', sources=['lltm.cpp'], - include_dirs=torch.utils.cpp_extension.include_paths(), + include_dirs=cpp_extension.include_paths(), language='c++') :class:`BuildExtension` performs a number of required configuration steps and @@ -413,7 +412,7 @@ see:: If we call ``help()`` on the function or module, we can see that its signature matches our C++ code:: - In[4] help(lltm.forward) + In[4] help(lltm_cpp.forward) forward(...) method of builtins.PyCapsule instance forward(arg0: torch::Tensor, arg1: torch::Tensor, arg2: torch::Tensor, arg3: torch::Tensor, arg4: torch::Tensor) -> List[torch::Tensor] @@ -473,6 +472,8 @@ small benchmark to see how much performance we gained from rewriting our op in C++. We'll run the LLTM forwards and backwards a few times and measure the duration:: + import time + import torch batch_size = 16 diff --git a/advanced_source/super_resolution_with_caffe2.py b/advanced_source/super_resolution_with_caffe2.py deleted file mode 100644 index 866a5f06081..00000000000 --- a/advanced_source/super_resolution_with_caffe2.py +++ /dev/null @@ -1,407 +0,0 @@ -""" -Transfering a Model from PyTorch to Caffe2 and Mobile using ONNX -================================================================ - -In this tutorial, we describe how to use ONNX to convert a model defined -in PyTorch into the ONNX format and then load it into Caffe2. Once in -Caffe2, we can run the model to double-check it was exported correctly, -and we then show how to use Caffe2 features such as mobile exporter for -executing the model on mobile devices. - -For this tutorial, you will need to install `onnx `__ -and `Caffe2 `__. -You can get binary builds of onnx with -``pip install onnx``. - -``NOTE``: This tutorial needs PyTorch master branch which can be installed by following -the instructions `here `__ - -""" - -# Some standard imports -import io -import numpy as np - -from torch import nn -import torch.utils.model_zoo as model_zoo -import torch.onnx - - -###################################################################### -# Super-resolution is a way of increasing the resolution of images, videos -# and is widely used in image processing or video editing. For this -# tutorial, we will first use a small super-resolution model with a dummy -# input. -# -# First, let's create a SuperResolution model in PyTorch. `This -# model `__ -# comes directly from PyTorch's examples without modification: -# - -# Super Resolution model definition in PyTorch -import torch.nn as nn -import torch.nn.init as init - - -class SuperResolutionNet(nn.Module): - def __init__(self, upscale_factor, inplace=False): - super(SuperResolutionNet, self).__init__() - - self.relu = nn.ReLU(inplace=inplace) - self.conv1 = nn.Conv2d(1, 64, (5, 5), (1, 1), (2, 2)) - self.conv2 = nn.Conv2d(64, 64, (3, 3), (1, 1), (1, 1)) - self.conv3 = nn.Conv2d(64, 32, (3, 3), (1, 1), (1, 1)) - self.conv4 = nn.Conv2d(32, upscale_factor ** 2, (3, 3), (1, 1), (1, 1)) - self.pixel_shuffle = nn.PixelShuffle(upscale_factor) - - self._initialize_weights() - - def forward(self, x): - x = self.relu(self.conv1(x)) - x = self.relu(self.conv2(x)) - x = self.relu(self.conv3(x)) - x = self.pixel_shuffle(self.conv4(x)) - return x - - def _initialize_weights(self): - init.orthogonal_(self.conv1.weight, init.calculate_gain('relu')) - init.orthogonal_(self.conv2.weight, init.calculate_gain('relu')) - init.orthogonal_(self.conv3.weight, init.calculate_gain('relu')) - init.orthogonal_(self.conv4.weight) - -# Create the super-resolution model by using the above model definition. -torch_model = SuperResolutionNet(upscale_factor=3) - - -###################################################################### -# Ordinarily, you would now train this model; however, for this tutorial, -# we will instead download some pre-trained weights. Note that this model -# was not trained fully for good accuracy and is used here for -# demonstration purposes only. -# - -# Load pretrained model weights -model_url = 'https://s3.amazonaws.com/pytorch/test_data/export/superres_epoch100-44c6958e.pth' -batch_size = 1 # just a random number - -# Initialize model with the pretrained weights -map_location = lambda storage, loc: storage -if torch.cuda.is_available(): - map_location = None -torch_model.load_state_dict(model_zoo.load_url(model_url, map_location=map_location)) - -# set the train mode to false since we will only run the forward pass. -torch_model.train(False) - - -###################################################################### -# Exporting a model in PyTorch works via tracing. To export a model, you -# call the ``torch.onnx._export()`` function. This will execute the model, -# recording a trace of what operators are used to compute the outputs. -# Because ``_export`` runs the model, we need provide an input tensor -# ``x``. The values in this tensor are not important; it can be an image -# or a random tensor as long as it is the right size. -# -# To learn more details about PyTorch's export interface, check out the -# `torch.onnx documentation `__. -# - -# Input to the model -x = torch.randn(batch_size, 1, 224, 224, requires_grad=True) - -# Export the model -torch_out = torch.onnx._export(torch_model, # model being run - x, # model input (or a tuple for multiple inputs) - "super_resolution.onnx", # where to save the model (can be a file or file-like object) - export_params=True) # store the trained parameter weights inside the model file - - -###################################################################### -# ``torch_out`` is the output after executing the model. Normally you can -# ignore this output, but here we will use it to verify that the model we -# exported computes the same values when run in Caffe2. -# -# Now let's take the ONNX representation and use it in Caffe2. This part -# can normally be done in a separate process or on another machine, but we -# will continue in the same process so that we can verify that Caffe2 and -# PyTorch are computing the same value for the network: -# - -import onnx -import caffe2.python.onnx.backend as onnx_caffe2_backend - -# Load the ONNX ModelProto object. model is a standard Python protobuf object -model = onnx.load("super_resolution.onnx") - -# prepare the caffe2 backend for executing the model this converts the ONNX model into a -# Caffe2 NetDef that can execute it. Other ONNX backends, like one for CNTK will be -# availiable soon. -prepared_backend = onnx_caffe2_backend.prepare(model) - -# run the model in Caffe2 - -# Construct a map from input names to Tensor data. -# The graph of the model itself contains inputs for all weight parameters, after the input image. -# Since the weights are already embedded, we just need to pass the input image. -# Set the first input. -W = {model.graph.input[0].name: x.data.numpy()} - -# Run the Caffe2 net: -c2_out = prepared_backend.run(W)[0] - -# Verify the numerical correctness upto 3 decimal places -np.testing.assert_almost_equal(torch_out.data.cpu().numpy(), c2_out, decimal=3) - -print("Exported model has been executed on Caffe2 backend, and the result looks good!") - -###################################################################### -# We should see that the output of PyTorch and Caffe2 runs match -# numerically up to 3 decimal places. As a side-note, if they do not match -# then there is an issue that the operators in Caffe2 and PyTorch are -# implemented differently and please contact us in that case. -# - - -###################################################################### -# Transfering SRResNet using ONNX -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# - - -###################################################################### -# Using the same process as above, we also transferred an interesting new -# model "SRResNet" for super-resolution presented in `this -# paper `__ (thanks to the authors -# at Twitter for providing us code and pretrained parameters for the -# purpose of this tutorial). The model definition and a pre-trained model -# can be found -# `here `__. -# Below is what SRResNet model input, output looks like. |SRResNet| -# -# .. |SRResNet| image:: /_static/img/SRResNet.png -# - - -###################################################################### -# Running the model on mobile devices -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# - - -###################################################################### -# So far we have exported a model from PyTorch and shown how to load it -# and run it in Caffe2. Now that the model is loaded in Caffe2, we can -# convert it into a format suitable for `running on mobile -# devices `__. -# -# We will use Caffe2's -# `mobile\_exporter `__ -# to generate the two model protobufs that can run on mobile. The first is -# used to initialize the network with the correct weights, and the second -# actual runs executes the model. We will continue to use the small -# super-resolution model for the rest of this tutorial. -# - -# extract the workspace and the model proto from the internal representation -c2_workspace = prepared_backend.workspace -c2_model = prepared_backend.predict_net - -# Now import the caffe2 mobile exporter -from caffe2.python.predictor import mobile_exporter - -# call the Export to get the predict_net, init_net. These nets are needed for running things on mobile -init_net, predict_net = mobile_exporter.Export(c2_workspace, c2_model, c2_model.external_input) - -# Let's also save the init_net and predict_net to a file that we will later use for running them on mobile -with open('init_net.pb', "wb") as fopen: - fopen.write(init_net.SerializeToString()) -with open('predict_net.pb', "wb") as fopen: - fopen.write(predict_net.SerializeToString()) - - -###################################################################### -# ``init_net`` has the model parameters and the model input embedded in it -# and ``predict_net`` will be used to guide the ``init_net`` execution at -# run-time. In this tutorial, we will use the ``init_net`` and -# ``predict_net`` generated above and run them in both normal Caffe2 -# backend and mobile and verify that the output high-resolution cat image -# produced in both runs is the same. -# -# For this tutorial, we will use a famous cat image used widely which -# looks like below -# -# .. figure:: /_static/img/cat_224x224.jpg -# :alt: cat -# - -# Some standard imports -from caffe2.proto import caffe2_pb2 -from caffe2.python import core, net_drawer, net_printer, visualize, workspace, utils - -import numpy as np -import os -import subprocess -from PIL import Image -from matplotlib import pyplot -from skimage import io, transform - - -###################################################################### -# First, let's load the image, pre-process it using standard skimage -# python library. Note that this preprocessing is the standard practice of -# processing data for training/testing neural networks. -# - -# load the image -img_in = io.imread("./_static/img/cat.jpg") - -# resize the image to dimensions 224x224 -img = transform.resize(img_in, [224, 224]) - -# save this resized image to be used as input to the model -io.imsave("./_static/img/cat_224x224.jpg", img) - - -###################################################################### -# Now, as a next step, let's take the resized cat image and run the -# super-resolution model in Caffe2 backend and save the output image. The -# image processing steps below have been adopted from PyTorch -# implementation of super-resolution model -# `here `__ -# - -# load the resized image and convert it to Ybr format -img = Image.open("./_static/img/cat_224x224.jpg") -img_ycbcr = img.convert('YCbCr') -img_y, img_cb, img_cr = img_ycbcr.split() - -# Let's run the mobile nets that we generated above so that caffe2 workspace is properly initialized -workspace.RunNetOnce(init_net) -workspace.RunNetOnce(predict_net) - -# Caffe2 has a nice net_printer to be able to inspect what the net looks like and identify -# what our input and output blob names are. -print(net_printer.to_string(predict_net)) - - -###################################################################### -# From the above output, we can see that input is named "9" and output is -# named "27"(it is a little bit weird that we will have numbers as blob -# names but this is because the tracing JIT produces numbered entries for -# the models) -# - -# Now, let's also pass in the resized cat image for processing by the model. -workspace.FeedBlob("9", np.array(img_y)[np.newaxis, np.newaxis, :, :].astype(np.float32)) - -# run the predict_net to get the model output -workspace.RunNetOnce(predict_net) - -# Now let's get the model output blob -img_out = workspace.FetchBlob("27") - - -###################################################################### -# Now, we'll refer back to the post-processing steps in PyTorch -# implementation of super-resolution model -# `here `__ -# to construct back the final output image and save the image. -# - -img_out_y = Image.fromarray(np.uint8((img_out[0, 0]).clip(0, 255)), mode='L') - -# get the output image follow post-processing step from PyTorch implementation -final_img = Image.merge( - "YCbCr", [ - img_out_y, - img_cb.resize(img_out_y.size, Image.BICUBIC), - img_cr.resize(img_out_y.size, Image.BICUBIC), - ]).convert("RGB") - -# Save the image, we will compare this with the output image from mobile device -final_img.save("./_static/img/cat_superres.jpg") - - -###################################################################### -# We have finished running our mobile nets in pure Caffe2 backend and now, -# let's execute the model on an Android device and get the model output. -# -# ``NOTE``: for Android development, ``adb`` shell is needed otherwise the -# following section of tutorial will not run. -# -# In our first step of runnig model on mobile, we will push a native speed -# benchmark binary for mobile device to adb. This binary can execute the -# model on mobile and also export the model output that we can retrieve -# later. The binary is available -# `here `__. -# In order to build the binary, execute the ``build_android.sh`` script -# following the instructions -# `here `__. -# -# ``NOTE``: You need to have ``ANDROID_NDK`` installed and set your env -# variable ``ANDROID_NDK=path to ndk root`` -# - -# let's first push a bunch of stuff to adb, specify the path for the binary -CAFFE2_MOBILE_BINARY = ('caffe2/binaries/speed_benchmark') - -# we had saved our init_net and proto_net in steps above, we use them now. -# Push the binary and the model protos -os.system('adb push ' + CAFFE2_MOBILE_BINARY + ' /data/local/tmp/') -os.system('adb push init_net.pb /data/local/tmp') -os.system('adb push predict_net.pb /data/local/tmp') - -# Let's serialize the input image blob to a blob proto and then send it to mobile for execution. -with open("input.blobproto", "wb") as fid: - fid.write(workspace.SerializeBlob("9")) - -# push the input image blob to adb -os.system('adb push input.blobproto /data/local/tmp/') - -# Now we run the net on mobile, look at the speed_benchmark --help for what various options mean -os.system( - 'adb shell /data/local/tmp/speed_benchmark ' # binary to execute - '--init_net=/data/local/tmp/super_resolution_mobile_init.pb ' # mobile init_net - '--net=/data/local/tmp/super_resolution_mobile_predict.pb ' # mobile predict_net - '--input=9 ' # name of our input image blob - '--input_file=/data/local/tmp/input.blobproto ' # serialized input image - '--output_folder=/data/local/tmp ' # destination folder for saving mobile output - '--output=27,9 ' # output blobs we are interested in - '--iter=1 ' # number of net iterations to execute - '--caffe2_log_level=0 ' -) - -# get the model output from adb and save to a file -os.system('adb pull /data/local/tmp/27 ./output.blobproto') - - -# We can recover the output content and post-process the model using same steps as we followed earlier -blob_proto = caffe2_pb2.BlobProto() -blob_proto.ParseFromString(open('./output.blobproto').read()) -img_out = utils.Caffe2TensorToNumpyArray(blob_proto.tensor) -img_out_y = Image.fromarray(np.uint8((img_out[0,0]).clip(0, 255)), mode='L') -final_img = Image.merge( - "YCbCr", [ - img_out_y, - img_cb.resize(img_out_y.size, Image.BICUBIC), - img_cr.resize(img_out_y.size, Image.BICUBIC), - ]).convert("RGB") -final_img.save("./_static/img/cat_superres_mobile.jpg") - - -###################################################################### -# Now, you can compare the image ``cat_superres.jpg`` (model output from -# pure caffe2 backend execution) and ``cat_superres_mobile.jpg`` (model -# output from mobile execution) and see that both the images look same. If -# they don't look same, something went wrong with execution on mobile and -# in that case, please contact Caffe2 community. You should expect to see -# the output image to look like following: -# -# .. figure:: /_static/img/cat_output1.png -# :alt: output\_cat -# -# -# Using the above steps, you can deploy your models on mobile easily. -# Also, for more information on caffe2 mobile backend, checkout -# `caffe2-android-demo `__. -# diff --git a/advanced_source/super_resolution_with_onnxruntime.py b/advanced_source/super_resolution_with_onnxruntime.py new file mode 100644 index 00000000000..88977ca9bd0 --- /dev/null +++ b/advanced_source/super_resolution_with_onnxruntime.py @@ -0,0 +1,312 @@ +""" +Exporting a Model from PyTorch to ONNX and Running it using ONNXRuntime +======================================================================= + +In this tutorial, we describe how to convert a model defined +in PyTorch into the ONNX format and then run it with ONNXRuntime. + +ONNXRuntime is a performance-focused engine for ONNX models, +which inferences efficiently across multiple platforms and hardware +(Windows, Linux, and Mac and on both CPUs and GPUs). +ONNXRuntime has proved to considerably increase performance over +multiple models as explained `here +`__ + +For this tutorial, you will need to install `onnx `__ +and `onnxruntime `__. +You can get binary builds of onnx and onnxrunimte with +``pip install onnx onnxruntime``. +Note that ONNXRuntime is compatible with Python versions 3.5 to 3.7. + +``NOTE``: This tutorial needs PyTorch master branch which can be installed by following +the instructions `here `__ + +""" + +# Some standard imports +import io +import numpy as np + +from torch import nn +import torch.utils.model_zoo as model_zoo +import torch.onnx + + +###################################################################### +# Super-resolution is a way of increasing the resolution of images, videos +# and is widely used in image processing or video editing. For this +# tutorial, we will use a small super-resolution model. +# +# First, let's create a SuperResolution model in PyTorch. +# This model uses the efficient sub-pixel convolution layer described in +# `"Real-Time Single Image and Video Super-Resolution Using an Efficient +# Sub-Pixel Convolutional Neural Network" - Shi et al `__ +# for increasing the resolution of an image by an upscale factor. +# The model expects the Y component of the YCbCr of an image as an input, and +# outputs the upscaled Y component in super resolution. +# +# `The +# model `__ +# comes directly from PyTorch's examples without modification: +# + +# Super Resolution model definition in PyTorch +import torch.nn as nn +import torch.nn.init as init + + +class SuperResolutionNet(nn.Module): + def __init__(self, upscale_factor, inplace=False): + super(SuperResolutionNet, self).__init__() + + self.relu = nn.ReLU(inplace=inplace) + self.conv1 = nn.Conv2d(1, 64, (5, 5), (1, 1), (2, 2)) + self.conv2 = nn.Conv2d(64, 64, (3, 3), (1, 1), (1, 1)) + self.conv3 = nn.Conv2d(64, 32, (3, 3), (1, 1), (1, 1)) + self.conv4 = nn.Conv2d(32, upscale_factor ** 2, (3, 3), (1, 1), (1, 1)) + self.pixel_shuffle = nn.PixelShuffle(upscale_factor) + + self._initialize_weights() + + def forward(self, x): + x = self.relu(self.conv1(x)) + x = self.relu(self.conv2(x)) + x = self.relu(self.conv3(x)) + x = self.pixel_shuffle(self.conv4(x)) + return x + + def _initialize_weights(self): + init.orthogonal_(self.conv1.weight, init.calculate_gain('relu')) + init.orthogonal_(self.conv2.weight, init.calculate_gain('relu')) + init.orthogonal_(self.conv3.weight, init.calculate_gain('relu')) + init.orthogonal_(self.conv4.weight) + +# Create the super-resolution model by using the above model definition. +torch_model = SuperResolutionNet(upscale_factor=3) + + +###################################################################### +# Ordinarily, you would now train this model; however, for this tutorial, +# we will instead download some pre-trained weights. Note that this model +# was not trained fully for good accuracy and is used here for +# demonstration purposes only. +# +# It is important to call ``torch_model.eval()`` or ``torch_model.train(False)`` +# before exporting the model, to turn the model to inference mode. +# This is required since operators like dropout or batchnorm behave +# differently in inference and training mode. +# + +# Load pretrained model weights +model_url = 'https://s3.amazonaws.com/pytorch/test_data/export/superres_epoch100-44c6958e.pth' +batch_size = 1 # just a random number + +# Initialize model with the pretrained weights +map_location = lambda storage, loc: storage +if torch.cuda.is_available(): + map_location = None +torch_model.load_state_dict(model_zoo.load_url(model_url, map_location=map_location)) + +# set the model to inference mode +torch_model.eval() + + +###################################################################### +# Exporting a model in PyTorch works via tracing or scripting. This +# tutorial will use as an example a model exported by tracing. +# To export a model, we call the ``torch.onnx.export()`` function. +# This will execute the model, recording a trace of what operators +# are used to compute the outputs. +# Because ``export`` runs the model, we need to provide an input +# tensor ``x``. The values in this can be random as long as it is the +# right type and size. +# Note that the input size will be fixed in the exported ONNX graph for +# all the input's dimensions, unless specified as a dynamic axes. +# In this example we export the model with an input of batch_size 1, +# but then specify the first dimension as dynamic in the ``dynamic_axes`` +# parameter in ``torch.onnx.export()``. +# The exported model will thus accept inputs of size [batch_size, 1, 224, 224] +# where batch_size can be variable. +# +# To learn more details about PyTorch's export interface, check out the +# `torch.onnx documentation `__. +# + +# Input to the model +x = torch.randn(batch_size, 1, 224, 224, requires_grad=True) +torch_out = torch_model(x) + +# Export the model +torch.onnx.export(torch_model, # model being run + x, # model input (or a tuple for multiple inputs) + "super_resolution.onnx", # where to save the model (can be a file or file-like object) + export_params=True, # store the trained parameter weights inside the model file + opset_version=10, # the onnx version to export the model to + do_constant_folding=True, # wether to execute constant folding for optimization + input_names = ['input'], # the model's input names + output_names = ['output'], # the model's output names + dynamic_axes={'input' : {0 : 'batch_size'}, # variable lenght axes + 'output' : {0 : 'batch_size'}}) + +###################################################################### +# We also computed ``torch_out``, the output after of the model, +# which we will use to verify that the model we exported computes +# the same values when run in onnxruntime. +# +# But before verifying the model's output with onnxruntime, we will check +# the onnx model with onnx's API. +# First, ``onnx.load("super_resolution.onnx")`` will load the saved model and +# will output a onnx.ModelProto structure (a top-level file/container format for bundling a ML model. +# For more information `onnx.proto documentation `__.). +# Then, ``onnx.checker.check_model(onnx_model)`` will verify the model's structure +# and confirm that the model has a valid schema. +# The validity of the ONNX graph is verified by checking the model's +# version, the graph's structure, as well as the nodes and their inputs +# and outputs. +# + +import onnx + +onnx_model = onnx.load("super_resolution.onnx") +onnx.checker.check_model(onnx_model) + + +###################################################################### +# Now let's compute the output using ONNXRuntime's Python APIs. +# This part can normally be done in a separate process or on another +# machine, but we will continue in the same process so that we can +# verify that onnxruntime and PyTorch are computing the same value +# for the network. +# +# In order to run the model with ONNXRuntime, we need to create an +# inference session for the model with the chosen configuration +# parameters (here we use the default config). +# Once the session is created, we evaluate the model using the run() api. +# The output of this call is a list containing the outputs of the model +# computed by ONNXRuntime. +# + +import onnxruntime + +ort_session = onnxruntime.InferenceSession("super_resolution.onnx") + +def to_numpy(tensor): + return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy() + +# compute onnxruntime output prediction +ort_inputs = {ort_session.get_inputs()[0].name: to_numpy(x)} +ort_outs = ort_session.run(None, ort_inputs) + +# compare onnxruntime and PyTorch results +np.testing.assert_allclose(to_numpy(torch_out), ort_outs[0], rtol=1e-03, atol=1e-05) + +print("Exported model has been tested with ONNXRuntime, and the result looks good!") + + +###################################################################### +# We should see that the output of PyTorch and onnxruntime runs match +# numerically with the given precision (rtol=1e-03 and atol=1e-05). +# As a side-note, if they do not match then there is an issue in the +# onnx exporter, so please contact us in that case. +# + + +###################################################################### +# Running the model on an image using ONNXRuntime +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# + + +###################################################################### +# So far we have exported a model from PyTorch and shown how to load it +# and run it in onnxruntime with a dummy tensor as an input. + +###################################################################### +# For this tutorial, we will use a famous cat image used widely which +# looks like below +# +# .. figure:: /_static/img/cat_224x224.jpg +# :alt: cat +# + +###################################################################### +# First, let's load the image, pre-process it using standard PIL +# python library. Note that this preprocessing is the standard practice of +# processing data for training/testing neural networks. +# +# We first resize the image to fit the size of the model's input (224x224). +# Then we split the image into its Y, Cb, and Cr components. +# These components represent a greyscale image (Y), and +# the blue-difference (Cb) and red-difference (Cr) chroma components. +# The Y component being more sensitive to the human eye, we are +# interested in this component which we will be transforming. +# After extracting the Y component, we convert it to a tensor which +# will be the input of our model. +# + +from PIL import Image +import torchvision.transforms as transforms + +img = Image.open("./_static/img/cat.jpg") + +resize = transforms.Resize([224, 224]) +img = resize(img) + +img_ycbcr = img.convert('YCbCr') +img_y, img_cb, img_cr = img_ycbcr.split() + +to_tensor = transforms.ToTensor() +img_y = to_tensor(img_y) +img_y.unsqueeze_(0) + + +###################################################################### +# Now, as a next step, let's take the tensor representing the +# greyscale resized cat image and run the super-resolution model in +# ONNXRuntime as explained previously. +# + +ort_inputs = {ort_session.get_inputs()[0].name: to_numpy(img_y)} +ort_outs = ort_session.run(None, ort_inputs) +img_out_y = ort_outs[0] + + +###################################################################### +# At this point, the output of the model is a tensor. +# Now, we'll process the output of the model to construct back the +# final output image from the output tensor, and save the image. +# The post-processing steps have been adopted from PyTorch +# implementation of super-resolution model +# `here `__. +# + +img_out_y = Image.fromarray(np.uint8((img_out_y[0] * 255.0).clip(0, 255)[0]), mode='L') + +# get the output image follow post-processing step from PyTorch implementation +final_img = Image.merge( + "YCbCr", [ + img_out_y, + img_cb.resize(img_out_y.size, Image.BICUBIC), + img_cr.resize(img_out_y.size, Image.BICUBIC), + ]).convert("RGB") + +# Save the image, we will compare this with the output image from mobile device +final_img.save("./_static/img/cat_superres_with_ort.jpg") + + +###################################################################### +# .. figure:: /_static/img/cat_superres_with_ort.jpg +# :alt: output\_cat +# +# +# ONNXRuntime being a cross platform engine, you can run it across +# multiple platforms and on both CPUs and GPUs. +# +# ONNXRuntime can also be deployed to the cloud for model inferencing +# using Azure Machine Learning Services. More information `here `__. +# +# More information about ONNXRuntime's performance `here `__. +# +# +# For more information about ONNXRuntime `here `__. +# diff --git a/advanced_source/torch_script_custom_ops.rst b/advanced_source/torch_script_custom_ops.rst index dd8dea4470d..1ad7b73013a 100644 --- a/advanced_source/torch_script_custom_ops.rst +++ b/advanced_source/torch_script_custom_ops.rst @@ -999,6 +999,11 @@ Naturally, it is best practice to only use ``torch.utils.cpp_extension.load_inline`` if your source code is reasonably short. +Note that if you're using this in a Jupyter Notebook, you should not execute +the cell with the registration multiple times because each execution registers +a new library and re-registers the custom operator. If you need to re-execute it, +please restart the Python kernel of your notebook beforehand. + Building with Setuptools ************************ diff --git a/beginner_source/Intro_to_TorchScript.py b/beginner_source/Intro_to_TorchScript.py new file mode 100644 index 00000000000..0b79b9b5000 --- /dev/null +++ b/beginner_source/Intro_to_TorchScript.py @@ -0,0 +1,384 @@ +""" +Introduction to TorchScript +=========================== + +*James Reed (jamesreed@fb.com), Michael Suo (suo@fb.com)*, rev2 + +In this tutorial we will cover: + +1. The basics of model authoring in PyTorch, including: + +- Modules +- Defining ``forward`` functions +- Composing modules into a hierarchy of modules + +2. Methods for converting PyTorch modules to TorchScript, our + high-performance deployment runtime + +- Tracing an existing module +- Using scripting to directly compile a module +- How to compose both approaches +- Saving and loading TorchScript modules + +""" + +import torch # This is all you need to use both PyTorch and TorchScript! +print(torch.__version__) + + +###################################################################### +# Basics of PyTorch Model Authoring +# --------------------------------- +# +# Let’s start out be defining a simple ``Module``. A ``Module`` is the +# basic unit of composition in PyTorch. It contains: +# +# 1. A constructor, which prepares the module for invocation +# 2. A set of ``Parameters`` and sub-\ ``Modules``. These are initialized +# by the constructor and can be used by the module during invocation. +# 3. A ``forward`` function. This is the code that is run when the module +# is invoked. +# +# Let’s examine a small example: +# + +class MyCell(torch.nn.Module): + def __init__(self): + super(MyCell, self).__init__() + + def forward(self, x, h): + new_h = torch.tanh(x + h) + return new_h, new_h + +my_cell = MyCell() +x = torch.rand(3, 4) +h = torch.rand(3, 4) +print(my_cell(x, h)) + + +###################################################################### +# So we’ve: +# +# 1. Created a class that subclasses ``torch.nn.Module``. +# 2. Defined a constructor. The constructor doesn’t do much, just calls +# the constructor for ``super``. +# 3. Defined a ``forward`` function, which takes two inputs and returns +# two outputs. The actual contents of the ``forward`` function are not +# really important, but it’s sort of a fake `RNN +# cell `__–that +# is–it’s a function that is applied on a loop. +# +# We instantiated the module, and made ``x`` and ``y``, which are just 3x4 +# matrices of random values. Then we invoked the cell with +# ``my_cell(x, h)``. This in turn calls our ``forward`` function. +# +# Let’s do something a little more interesting: +# + +class MyCell(torch.nn.Module): + def __init__(self): + super(MyCell, self).__init__() + self.linear = torch.nn.Linear(4, 4) + + def forward(self, x, h): + new_h = torch.tanh(self.linear(x) + h) + return new_h, new_h + +my_cell = MyCell() +print(my_cell) +print(my_cell(x, h)) + + +###################################################################### +# We’ve redefined our module ``MyCell``, but this time we’ve added a +# ``self.linear`` attribute, and we invoke ``self.linear`` in the forward +# function. +# +# What exactly is happening here? ``torch.nn.Linear`` is a ``Module`` from +# the PyTorch standard library. Just like ``MyCell``, it can be invoked +# using the call syntax. We are building a hierarchy of ``Module``\ s. +# +# ``print`` on a ``Module`` will give a visual representation of the +# ``Module``\ ’s subclass hierarchy. In our example, we can see our +# ``Linear`` subclass and its parameters. +# +# By composing ``Module``\ s in this way, we can succintly and readably +# author models with reusable components. +# +# You may have noticed ``grad_fn`` on the outputs. This is a detail of +# PyTorch’s method of automatic differentiation, called +# `autograd `__. +# In short, this system allows us to compute derivatives through +# potentially complex programs. The design allows for a massive amount of +# flexibility in model authoring. +# +# Now let’s examine said flexibility: +# + +class MyDecisionGate(torch.nn.Module): + def forward(self, x): + if x.sum() > 0: + return x + else: + return -x + +class MyCell(torch.nn.Module): + def __init__(self): + super(MyCell, self).__init__() + self.dg = MyDecisionGate() + self.linear = torch.nn.Linear(4, 4) + + def forward(self, x, h): + new_h = torch.tanh(self.dg(self.linear(x)) + h) + return new_h, new_h + +my_cell = MyCell() +print(my_cell) +print(my_cell(x, h)) + + +###################################################################### +# We’ve once again redefined our MyCell class, but here we’ve defined +# ``MyDecisionGate``. This module utilizes **control flow**. Control flow +# consists of things like loops and ``if``-statements. +# +# Many frameworks take the approach of computing symbolic derivatives +# given a full program representation. However, in PyTorch, we use a +# gradient tape. We record operations as they occur, and replay them +# backwards in computing derivatives. In this way, the framework does not +# have to explicitly define derivatives for all constructs in the +# language. +# +# .. figure:: https://github.com/pytorch/pytorch/raw/master/docs/source/_static/img/dynamic_graph.gif +# :alt: How autograd works +# +# How autograd works +# + + +###################################################################### +# Basics of TorchScript +# --------------------- +# +# Now let’s take our running example and see how we can apply TorchScript. +# +# In short, TorchScript provides tools to capture the definition of your +# model, even in light of the flexible and dynamic nature of PyTorch. +# Let’s begin by examining what we call **tracing**. +# +# Tracing ``Modules`` +# ~~~~~~~~~~~~~~~~~~~ +# + +class MyCell(torch.nn.Module): + def __init__(self): + super(MyCell, self).__init__() + self.linear = torch.nn.Linear(4, 4) + + def forward(self, x, h): + new_h = torch.tanh(self.linear(x) + h) + return new_h, new_h + +my_cell = MyCell() +x, h = torch.rand(3, 4), torch.rand(3, 4) +traced_cell = torch.jit.trace(my_cell, (x, h)) +print(traced_cell) +traced_cell(x, h) + + +###################################################################### +# We’ve rewinded a bit and taken the second version of our ``MyCell`` +# class. As before, we’ve instantiated it, but this time, we’ve called +# ``torch.jit.trace``, passed in the ``Module``, and passed in *example +# inputs* the network might see. +# +# What exactly has this done? It has invoked the ``Module``, recorded the +# operations that occured when the ``Module`` was run, and created an +# instance of ``torch.jit.ScriptModule`` (of which ``TracedModule`` is an +# instance) +# +# TorchScript records its definitions in an Intermediate Representation +# (or IR), commonly referred to in Deep learning as a *graph*. We can +# examine the graph with the ``.graph`` property: +# + +print(traced_cell.graph) + + +###################################################################### +# However, this is a very low-level representation and most of the +# information contained in the graph is not useful for end users. Instead, +# we can use the ``.code`` property to give a Python-syntax interpretation +# of the code: +# + +print(traced_cell.code) + + +###################################################################### +# So **why** did we do all this? There are several reasons: +# +# 1. TorchScript code can be invoked in its own interpreter, which is +# basically a restricted Python interpreter. This interpreter does not +# acquire the Global Interpreter Lock, and so many requests can be +# processed on the same instance simultaneously. +# 2. This format allows us to save the whole model to disk and load it +# into another environment, such as in a server written in a language +# other than Python +# 3. TorchScript gives us a representation in which we can do compiler +# optimizations on the code to provide more efficient execution +# 4. TorchScript allows us to interface with many backend/device runtimes +# that require a broader view of the program than individual operators. +# +# We can see that invoking ``traced_cell`` produces the same results as +# the Python module: +# + +print(my_cell(x, h)) +print(traced_cell(x, h)) + + +###################################################################### +# Using Scripting to Convert Modules +# ---------------------------------- +# +# There’s a reason we used version two of our module, and not the one with +# the control-flow-laden submodule. Let’s examine that now: +# + +class MyDecisionGate(torch.nn.Module): + def forward(self, x): + if x.sum() > 0: + return x + else: + return -x + +class MyCell(torch.nn.Module): + def __init__(self, dg): + super(MyCell, self).__init__() + self.dg = dg + self.linear = torch.nn.Linear(4, 4) + + def forward(self, x, h): + new_h = torch.tanh(self.dg(self.linear(x)) + h) + return new_h, new_h + +my_cell = MyCell(MyDecisionGate()) +traced_cell = torch.jit.trace(my_cell, (x, h)) +print(traced_cell.code) + + +###################################################################### +# Looking at the ``.code`` output, we can see that the ``if-else`` branch +# is nowhere to be found! Why? Tracing does exactly what we said it would: +# run the code, record the operations *that happen* and construct a +# ScriptModule that does exactly that. Unfortunately, things like control +# flow are erased. +# +# How can we faithfully represent this module in TorchScript? We provide a +# **script compiler**, which does direct analysis of your Python source +# code to transform it into TorchScript. Let’s convert ``MyDecisionGate`` +# using the script compiler: +# + +scripted_gate = torch.jit.script(MyDecisionGate()) + +my_cell = MyCell(scripted_gate) +traced_cell = torch.jit.script(my_cell) +print(traced_cell.code) + + +###################################################################### +# Hooray! We’ve now faithfully captured the behavior of our program in +# TorchScript. Let’s now try running the program: +# + +# New inputs +x, h = torch.rand(3, 4), torch.rand(3, 4) +traced_cell(x, h) + + +###################################################################### +# Mixing Scripting and Tracing +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Some situations call for using tracing rather than scripting (e.g. a +# module has many architectural decisions that are made based on constant +# Python values that we would like to not appear in TorchScript). In this +# case, scripting can be composed with tracing: ``torch.jit.script`` will +# inline the code for a traced module, and tracing will inline the code +# for a scripted module. +# +# An example of the first case: +# + +class MyRNNLoop(torch.nn.Module): + def __init__(self): + super(MyRNNLoop, self).__init__() + self.cell = torch.jit.trace(MyCell(scripted_gate), (x, h)) + + def forward(self, xs): + h, y = torch.zeros(3, 4), torch.zeros(3, 4) + for i in range(xs.size(0)): + y, h = self.cell(xs[i], h) + return y, h + +rnn_loop = torch.jit.script(MyRNNLoop()) +print(rnn_loop.code) + + + +###################################################################### +# And an example of the second case: +# + +class WrapRNN(torch.nn.Module): + def __init__(self): + super(WrapRNN, self).__init__() + self.loop = torch.jit.script(MyRNNLoop()) + + def forward(self, xs): + y, h = self.loop(xs) + return torch.relu(y) + +traced = torch.jit.trace(WrapRNN(), (torch.rand(10, 3, 4))) +print(traced.code) + + +###################################################################### +# This way, scripting and tracing can be used when the situation calls for +# each of them and used together. +# +# Saving and Loading models +# ------------------------- +# +# We provide APIs to save and load TorchScript modules to/from disk in an +# archive format. This format includes code, parameters, attributes, and +# debug information, meaning that the archive is a freestanding +# representation of the model that can be loaded in an entirely separate +# process. Let’s save and load our wrapped RNN module: +# + +traced.save('wrapped_rnn.zip') + +loaded = torch.jit.load('wrapped_rnn.zip') + +print(loaded) +print(loaded.code) + + +###################################################################### +# As you can see, serialization preserves the module hierarchy and the +# code we’ve been examining throughout. The model can also be loaded, for +# example, `into +# C++ `__ for +# python-free execution. +# +# Further Reading +# ~~~~~~~~~~~~~~~ +# +# We’ve completed our tutorial! For a more involved demonstration, check +# out the NeurIPS demo for converting machine translation models using +# TorchScript: +# https://colab.research.google.com/drive/1HiICg6jRkBnr5hvK2-VnMi88Vi9pUzEJ +# diff --git a/beginner_source/PyTorch Cheat.md b/beginner_source/PyTorch Cheat.md index 3101bd6a2d3..212cd73974d 100644 --- a/beginner_source/PyTorch Cheat.md +++ b/beginner_source/PyTorch Cheat.md @@ -26,13 +26,13 @@ from torch.jit import script, trace # hybrid frontend decorator and tracin ``` See [autograd](https://pytorch.org/docs/stable/autograd.html), [nn](https://pytorch.org/docs/stable/nn.html), [functional](https://pytorch.org/docs/stable/nn.html#torch-nn-functional) and [optim](https://pytorch.org/docs/stable/optim.html) -### Hybrid frontend +### Torchscript and JIT ``` torch.jit.trace() # takes your module or function and an example data input, and traces the computational steps that the data encounters as it progresses through the model @script # decorator used to indicate data-dependent control flow within the code being traced ``` -See [hybrid frontend](https://pytorch.org/docs/stable/hybridfrontend) +See [Torchscript](https://pytorch.org/docs/stable/jit.html) ### ONNX diff --git a/beginner_source/audio_classifier_tutorial.py b/beginner_source/audio_classifier_tutorial.py deleted file mode 100644 index 5a9b29a3108..00000000000 --- a/beginner_source/audio_classifier_tutorial.py +++ /dev/null @@ -1,310 +0,0 @@ -""" -Audio Classifier Tutorial -========================= -**Author**: `Winston Herring `_ - -This tutorial will show you how to correctly format an audio dataset and -then train/test an audio classifier network on the dataset. First, let’s -import the common torch packages as well as ``torchaudio``, ``pandas``, -and ``numpy``. ``torchaudio`` is available `here `_ -and can be installed by following the -instructions on the website. - -""" - -import torch -import torch.nn as nn -import torch.nn.functional as F -import torch.optim as optim -from torchvision import datasets, transforms -from torch.utils.data import Dataset -import torchaudio -import pandas as pd -import numpy as np - - -###################################################################### -# Let’s check if a CUDA GPU is available and select our device. Running -# the network on a GPU will greatly decrease the training/testing runtime. -# - -device = torch.device("cuda" if torch.cuda.is_available() else "cpu") -print(device) - - -###################################################################### -# Importing the Dataset -# --------------------- -# -# We will use the UrbanSound8K dataset to train our network. It is -# available for free `here `_ and contains -# 10 audio classes with over 8000 audio samples! Once you have downloaded -# the compressed dataset, extract it to your current working directory. -# First, we will look at the csv file that provides information about the -# individual sound files. ``pandas`` allows us to open the csv file and -# use ``.iloc()`` to access the data within it. -# - -csvData = pd.read_csv('./data/UrbanSound8K/metadata/UrbanSound8K.csv') -print(csvData.iloc[0, :]) - - -###################################################################### -# The 10 audio classes in the UrbanSound8K dataset are air_conditioner, -# car_horn, children_playing, dog_bark, drilling, enginge_idling, -# gun_shot, jackhammer, siren, and street_music. Let’s play a couple files -# and see what they sound like. The first file is street music and the -# second is an air conditioner. -# - -import IPython.display as ipd -ipd.Audio('./data/UrbanSound8K/audio/fold1/108041-9-0-5.wav') - -ipd.Audio('./data/UrbanSound8K/audio/fold5/100852-0-0-19.wav') - - -###################################################################### -# Formatting the Data -# ------------------- -# -# Now that we know the format of the csv file entries, we can construct -# our dataset. We will create a rapper class for our dataset using -# ``torch.utils.data.Dataset`` that will handle loading the files and -# performing some formatting steps. The UrbanSound8K dataset is separated -# into 10 folders. We will use the data from 9 of these folders to train -# our network and then use the 10th folder to test the network. The rapper -# class will store the file names, labels, and folder numbers of the audio -# files in the inputted folder list when initialized. The actual loading -# and formatting steps will happen in the access function ``__getitem__``. -# -# In ``__getitem__``, we use ``torchaudio.load()`` to convert the wav -# files to tensors. ``torchaudio.load()`` returns a tuple containing the -# newly created tensor along with the sampling frequency of the audio file -# (44.1kHz for UrbanSound8K). The dataset uses two channels for audio so -# we will use ``torchaudio.transforms.DownmixMono()`` to convert the audio -# data to one channel. Next, we need to format the audio data. The network -# we will make takes an input size of 32,000, while most of the audio -# files have well over 100,000 samples. The UrbanSound8K audio is sampled -# at 44.1kHz, so 32,000 samples only covers around 700 milliseconds. By -# downsampling the audio to aproximately 8kHz, we can represent 4 seconds -# with the 32,000 samples. This downsampling is achieved by taking every -# fifth sample of the original audio tensor. Not every audio tensor is -# long enough to handle the downsampling so these tensors will need to be -# padded with zeros. The minimum length that won’t require padding is -# 160,000 samples. -# - -class UrbanSoundDataset(Dataset): -#rapper for the UrbanSound8K dataset - # Argument List - # path to the UrbanSound8K csv file - # path to the UrbanSound8K audio files - # list of folders to use in the dataset - - def __init__(self, csv_path, file_path, folderList): - csvData = pd.read_csv(csv_path) - #initialize lists to hold file names, labels, and folder numbers - self.file_names = [] - self.labels = [] - self.folders = [] - #loop through the csv entries and only add entries from folders in the folder list - for i in range(0,len(csvData)): - if csvData.iloc[i, 5] in folderList: - self.file_names.append(csvData.iloc[i, 0]) - self.labels.append(csvData.iloc[i, 6]) - self.folders.append(csvData.iloc[i, 5]) - - self.file_path = file_path - self.mixer = torchaudio.transforms.DownmixMono() #UrbanSound8K uses two channels, this will convert them to one - self.folderList = folderList - - def __getitem__(self, index): - #format the file path and load the file - path = self.file_path + "fold" + str(self.folders[index]) + "/" + self.file_names[index] - sound = torchaudio.load(path, out = None, normalization = True) - #load returns a tensor with the sound data and the sampling frequency (44.1kHz for UrbanSound8K) - soundData = self.mixer(sound[0]) - #downsample the audio to ~8kHz - tempData = torch.zeros([160000, 1]) #tempData accounts for audio clips that are too short - if soundData.numel() < 160000: - tempData[:soundData.numel()] = soundData[:] - else: - tempData[:] = soundData[:160000] - - soundData = tempData - soundFormatted = torch.zeros([32000, 1]) - soundFormatted[:32000] = soundData[::5] #take every fifth sample of soundData - soundFormatted = soundFormatted.permute(1, 0) - return soundFormatted, self.labels[index] - - def __len__(self): - return len(self.file_names) - - -csv_path = './data/UrbanSound8K/metadata/UrbanSound8K.csv' -file_path = './data/UrbanSound8K/audio/' - -train_set = UrbanSoundDataset(csv_path, file_path, range(1,10)) -test_set = UrbanSoundDataset(csv_path, file_path, [10]) -print("Train set size: " + str(len(train_set))) -print("Test set size: " + str(len(test_set))) - -kwargs = {'num_workers': 1, 'pin_memory': True} if device == 'cuda' else {} #needed for using datasets on gpu - -train_loader = torch.utils.data.DataLoader(train_set, batch_size = 128, shuffle = True, **kwargs) -test_loader = torch.utils.data.DataLoader(test_set, batch_size = 128, shuffle = True, **kwargs) - - -###################################################################### -# Define the Network -# ------------------ -# -# For this tutorial we will use a convolutional neural network to process -# the raw audio data. Usually more advanced transforms are applied to the -# audio data, however CNNs can be used to accurately process the raw data. -# The specific architecture is modeled after the M5 network architecture -# described in https://arxiv.org/pdf/1610.00087.pdf. An important aspect -# of models processing raw audio data is the receptive field of their -# first layer’s filters. Our model’s first filter is length 80 so when -# processing audio sampled at 8kHz the receptive field is around 10ms. -# This size is similar to speech processing applications that often use -# receptive fields ranging from 20ms to 40ms. -# - -class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv1d(1, 128, 80, 4) - self.bn1 = nn.BatchNorm1d(128) - self.pool1 = nn.MaxPool1d(4) - self.conv2 = nn.Conv1d(128, 128, 3) - self.bn2 = nn.BatchNorm1d(128) - self.pool2 = nn.MaxPool1d(4) - self.conv3 = nn.Conv1d(128, 256, 3) - self.bn3 = nn.BatchNorm1d(256) - self.pool3 = nn.MaxPool1d(4) - self.conv4 = nn.Conv1d(256, 512, 3) - self.bn4 = nn.BatchNorm1d(512) - self.pool4 = nn.MaxPool1d(4) - self.avgPool = nn.AvgPool1d(30) #input should be 512x30 so this outputs a 512x1 - self.fc1 = nn.Linear(512, 10) - - def forward(self, x): - x = self.conv1(x) - x = F.relu(self.bn1(x)) - x = self.pool1(x) - x = self.conv2(x) - x = F.relu(self.bn2(x)) - x = self.pool2(x) - x = self.conv3(x) - x = F.relu(self.bn3(x)) - x = self.pool3(x) - x = self.conv4(x) - x = F.relu(self.bn4(x)) - x = self.pool4(x) - x = self.avgPool(x) - x = x.permute(0, 2, 1) #change the 512x1 to 1x512 - x = self.fc1(x) - return F.log_softmax(x, dim = 2) - -model = Net() -model.to(device) -print(model) - - -###################################################################### -# We will use the same optimization technique used in the paper, an Adam -# optimizer with weight decay set to 0.0001. At first, we will train with -# a learning rate of 0.01, but we will use a ``scheduler`` to decrease it -# to 0.001 during training. -# - -optimizer = optim.Adam(model.parameters(), lr = 0.01, weight_decay = 0.0001) -scheduler = optim.lr_scheduler.StepLR(optimizer, step_size = 20, gamma = 0.1) - - -###################################################################### -# Training and Testing the Network -# -------------------------------- -# -# Now let’s define a training function that will feed our training data -# into the model and perform the backward pass and optimization steps. -# - -def train(model, epoch): - model.train() - for batch_idx, (data, target) in enumerate(train_loader): - optimizer.zero_grad() - data = data.to(device) - target = target.to(device) - data = data.requires_grad_() #set requires_grad to True for training - output = model(data) - output = output.permute(1, 0, 2) #original output dimensions are batchSizex1x10 - loss = F.nll_loss(output[0], target) #the loss functions expects a batchSizex10 input - loss.backward() - optimizer.step() - if batch_idx % log_interval == 0: #print training stats - print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( - epoch, batch_idx * len(data), len(train_loader.dataset), - 100. * batch_idx / len(train_loader), loss)) - - -###################################################################### -# Now that we have a training function, we need to make one for testing -# the networks accuracy. We will set the model to ``eval()`` mode and then -# run inference on the test dataset. Calling ``eval()`` sets the training -# variable in all modules in the network to false. Certain layers like -# batch normalization and dropout layers behave differently during -# training so this step is crucial for getting correct results. -# - -def test(model, epoch): - model.eval() - correct = 0 - for data, target in test_loader: - data = data.to(device) - target = target.to(device) - output = model(data) - output = output.permute(1, 0, 2) - pred = output.max(2)[1] # get the index of the max log-probability - correct += pred.eq(target).cpu().sum().item() - print('\nTest set: Accuracy: {}/{} ({:.0f}%)\n'.format( - correct, len(test_loader.dataset), - 100. * correct / len(test_loader.dataset))) - - -###################################################################### -# Finally, we can train and test the network. We will train the network -# for ten epochs then reduce the learn rate and train for ten more epochs. -# The network will be tested after each epoch to see how the accuracy -# varies during the training. -# -# .. note:: Due to a build issue, we've reduced the number of epochs to 10. -# Run this sample with 40 locally to get the proper values. -# - -log_interval = 20 -for epoch in range(1, 11): - if epoch == 31: - print("First round of training complete. Setting learn rate to 0.001.") - scheduler.step() - train(model, epoch) - test(model, epoch) - - -###################################################################### -# Conclusion -# ---------- -# -# If trained on 9 folders, the network should be more than 50% accurate by -# the end of the training process. Training on less folders will result in -# a lower overall accuracy but may be necessary if long runtimes are a -# problem. Greater accuracies can be achieved using deeper CNNs at the -# expense of a larger memory footprint. -# -# For more advanced audio applications, such as speech recognition, -# recurrent neural networks (RNNs) are commonly used. There are also other -# data preprocessing methods, such as finding the mel frequency cepstral -# coefficients (MFCC), that can reduce the size of the dataset. -# - diff --git a/beginner_source/blitz/cifar10_tutorial.py b/beginner_source/blitz/cifar10_tutorial.py index d84e2fab7e0..981aec8cfee 100644 --- a/beginner_source/blitz/cifar10_tutorial.py +++ b/beginner_source/blitz/cifar10_tutorial.py @@ -235,7 +235,7 @@ def forward(self, x): 100 * correct / total)) ######################################################################## -# That looks waaay better than chance, which is 10% accuracy (randomly picking +# That looks way better than chance, which is 10% accuracy (randomly picking # a class out of 10 classes). # Seems like the network learnt something. # @@ -298,7 +298,7 @@ def forward(self, x): # inputs, labels = data[0].to(device), data[1].to(device) # # Why dont I notice MASSIVE speedup compared to CPU? Because your network -# is realllly small. +# is really small. # # **Exercise:** Try increasing the width of your network (argument 2 of # the first ``nn.Conv2d``, and argument 1 of the second ``nn.Conv2d`` – diff --git a/beginner_source/chatbot_tutorial.py b/beginner_source/chatbot_tutorial.py index 838a1c1aeda..991087b6c56 100644 --- a/beginner_source/chatbot_tutorial.py +++ b/beginner_source/chatbot_tutorial.py @@ -195,7 +195,8 @@ def loadConversations(fileName, lines, fields): for i, field in enumerate(fields): convObj[field] = values[i] # Convert string to list (convObj["utteranceIDs"] == "['L598485', 'L598486', ...]") - lineIds = eval(convObj["utteranceIDs"]) + utterance_id_pattern = re.compile('L[0-9]+') + lineIds = utterance_id_pattern.findall(convObj["utteranceIDs"]) # Reassemble lines convObj["lines"] = [] for lineId in lineIds: @@ -1329,6 +1330,17 @@ def evaluateInput(encoder, decoder, searcher, voc): encoder_optimizer.load_state_dict(encoder_optimizer_sd) decoder_optimizer.load_state_dict(decoder_optimizer_sd) +# If you have cuda, configure cuda to call +for state in encoder_optimizer.state.values(): + for k, v in state.items(): + if isinstance(v, torch.Tensor): + state[k] = v.cuda() + +for state in decoder_optimizer.state.values(): + for k, v in state.items(): + if isinstance(v, torch.Tensor): + state[k] = v.cuda() + # Run training iterations print("Starting Training!") trainIters(model_name, voc, pairs, encoder, decoder, encoder_optimizer, decoder_optimizer, diff --git a/beginner_source/dcgan_faces_tutorial.py b/beginner_source/dcgan_faces_tutorial.py index 018d67a3705..74e9d94406a 100644 --- a/beginner_source/dcgan_faces_tutorial.py +++ b/beginner_source/dcgan_faces_tutorial.py @@ -53,7 +53,7 @@ # :math:`D(x)` is the discriminator network which outputs the (scalar) # probability that :math:`x` came from training data rather than the # generator. Here, since we are dealing with images the input to -# :math:`D(x)` is an image of HWC size 3x64x64. Intuitively, :math:`D(x)` +# :math:`D(x)` is an image of CHW size 3x64x64. Intuitively, :math:`D(x)` # should be HIGH when :math:`x` comes from training data and LOW when # :math:`x` comes from the generator. :math:`D(x)` can also be thought of # as a traditional binary classifier. diff --git a/beginner_source/deploy_seq2seq_hybrid_frontend_tutorial.py b/beginner_source/deploy_seq2seq_hybrid_frontend_tutorial.py index c8273b24d7b..532720698e8 100644 --- a/beginner_source/deploy_seq2seq_hybrid_frontend_tutorial.py +++ b/beginner_source/deploy_seq2seq_hybrid_frontend_tutorial.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -Deploying a Seq2Seq Model with the Hybrid Frontend +Deploying a Seq2Seq Model with TorchScript ================================================== **Author:** `Matthew Inkawhich `_ """ @@ -8,9 +8,9 @@ ###################################################################### # This tutorial will walk through the process of transitioning a -# sequence-to-sequence model to Torch Script using PyTorch’s Hybrid -# Frontend. The model that we will convert is the chatbot model from the -# `Chatbot tutorial `__. +# sequence-to-sequence model to TorchScript using the TorchScript +# API. The model that we will convert is the chatbot model from the +# `Chatbot tutorial `__. # You can either treat this tutorial as a “Part 2” to the Chatbot tutorial # and deploy your own pretrained model, or you can start with this # document and use a pretrained model that we host. In the latter case, @@ -18,7 +18,7 @@ # regarding data preprocessing, model theory and definition, and model # training. # -# What is the Hybrid Frontend? +# What is TorchScript? # ---------------------------- # # During the research and development phase of a deep learning-based @@ -34,13 +34,13 @@ # to target highly optimized hardware architectures. Also, a graph-based # representation enables framework-agnostic model exportation. PyTorch # provides mechanisms for incrementally converting eager-mode code into -# Torch Script, a statically analyzable and optimizable subset of Python +# TorchScript, a statically analyzable and optimizable subset of Python # that Torch uses to represent deep learning programs independently from # the Python runtime. # -# The API for converting eager-mode PyTorch programs into Torch Script is +# The API for converting eager-mode PyTorch programs into TorchScript is # found in the torch.jit module. This module has two core modalities for -# converting an eager-mode model to a Torch Script graph representation: +# converting an eager-mode model to a TorchScript graph representation: # **tracing** and **scripting**. The ``torch.jit.trace`` function takes a # module or function and a set of example inputs. It then runs the example # input through the function or module while tracing the computational @@ -52,19 +52,19 @@ # operations called along the execution route taken by the example input # will be recorded. In other words, the control flow itself is not # captured. To convert modules and functions containing data-dependent -# control flow, a **scripting** mechanism is provided. Scripting -# explicitly converts the module or function code to Torch Script, -# including all possible control flow routes. To use script mode, be sure -# to inherit from the the ``torch.jit.ScriptModule`` base class (instead -# of ``torch.nn.Module``) and add a ``torch.jit.script`` decorator to your -# Python function or a ``torch.jit.script_method`` decorator to your -# module’s methods. The one caveat with using scripting is that it only -# supports a restricted subset of Python. For all details relating to the -# supported features, see the Torch Script `language -# reference `__. To provide the -# maximum flexibility, the modes of Torch Script can be composed to -# represent your whole program, and these techniques can be applied -# incrementally. +# control flow, a **scripting** mechanism is provided. The +# ``torch.jit.script`` function/decorator takes a module or function and +# does not requires example inputs. Scripting then explicitly converts +# the module or function code to TorchScript, including all control flows. +# One caveat with using scripting is that it only supports a subset of +# Python, so you might need to rewrite the code to make it compatible +# with the TorchScript syntax. +# +# For all details relating to the supported features, see the TorchScript +# `language reference `__. To +# provide the maximum flexibility, you can also mix tracing and scripting +# modes together to represent your whole program, and these techniques can +# be applied incrementally. # # .. figure:: /_static/img/chatbot/pytorch_workflow.png # :align: center @@ -273,7 +273,7 @@ def indexesFromSentence(voc, sentence): # used by the ``torch.nn.utils.rnn.pack_padded_sequence`` function when # padding. # -# Hybrid Frontend Notes: +# TorchScript Notes: # ~~~~~~~~~~~~~~~~~~~~~~ # # Since the encoder’s ``forward`` function does not contain any @@ -296,6 +296,7 @@ def __init__(self, hidden_size, embedding, n_layers=1, dropout=0): dropout=(0 if n_layers == 1 else dropout), bidirectional=True) def forward(self, input_seq, input_lengths, hidden=None): + # type: (Tensor, Tensor, Optional[Tensor]) -> Tuple[Tensor, Tensor] # Convert word indexes to embeddings embedded = self.embedding(input_seq) # Pack padded batch of sequences for RNN module @@ -325,7 +326,7 @@ def forward(self, input_seq, input_lengths, hidden=None): # # Luong attention layer -class Attn(torch.nn.Module): +class Attn(nn.Module): def __init__(self, method, hidden_size): super(Attn, self).__init__() self.method = method @@ -333,10 +334,10 @@ def __init__(self, method, hidden_size): raise ValueError(self.method, "is not an appropriate attention method.") self.hidden_size = hidden_size if self.method == 'general': - self.attn = torch.nn.Linear(self.hidden_size, hidden_size) + self.attn = nn.Linear(self.hidden_size, hidden_size) elif self.method == 'concat': - self.attn = torch.nn.Linear(self.hidden_size * 2, hidden_size) - self.v = torch.nn.Parameter(torch.FloatTensor(hidden_size)) + self.attn = nn.Linear(self.hidden_size * 2, hidden_size) + self.v = nn.Parameter(torch.FloatTensor(hidden_size)) def dot_score(self, hidden, encoder_output): return torch.sum(hidden * encoder_output, dim=2) @@ -383,14 +384,14 @@ def forward(self, hidden, encoder_outputs): # weighted sum indicating what parts of the encoder’s output to pay # attention to. From here, we use a linear layer and softmax normalization # to select the next word in the output sequence. -# -# Hybrid Frontend Notes: + +# TorchScript Notes: # ~~~~~~~~~~~~~~~~~~~~~~ # # Similarly to the ``EncoderRNN``, this module does not contain any # data-dependent control flow. Therefore, we can once again use -# **tracing** to convert this model to Torch Script after it is -# initialized and its parameters are loaded. +# **tracing** to convert this model to TorchScript after it +# is initialized and its parameters are loaded. # class LuongAttnDecoderRNN(nn.Module): @@ -465,18 +466,18 @@ def forward(self, input_step, last_hidden, encoder_outputs): # terminates either if the ``decoded_words`` list has reached a length of # *MAX_LENGTH* or if the predicted word is the *EOS_token*. # -# Hybrid Frontend Notes: +# TorchScript Notes: # ~~~~~~~~~~~~~~~~~~~~~~ # # The ``forward`` method of this module involves iterating over the range # of :math:`[0, max\_length)` when decoding an output sequence one word at # a time. Because of this, we should use **scripting** to convert this -# module to Torch Script. Unlike with our encoder and decoder models, +# module to TorchScript. Unlike with our encoder and decoder models, # which we can trace, we must make some necessary changes to the # ``GreedySearchDecoder`` module in order to initialize an object without # error. In other words, we must ensure that our module adheres to the -# rules of the scripting mechanism, and does not utilize any language -# features outside of the subset of Python that Torch Script includes. +# rules of the TorchScript mechanism, and does not utilize any language +# features outside of the subset of Python that TorchScript includes. # # To get an idea of some manipulations that may be required, we will go # over the diffs between the ``GreedySearchDecoder`` implementation from @@ -491,12 +492,6 @@ def forward(self, input_step, last_hidden, encoder_outputs): # Changes: # ^^^^^^^^ # -# - ``nn.Module`` -> ``torch.jit.ScriptModule`` -# -# - In order to use PyTorch’s scripting mechanism on a module, that -# module must inherit from the ``torch.jit.ScriptModule``. -# -# # - Added ``decoder_n_layers`` to the constructor arguments # # - This change stems from the fact that the encoder and decoder @@ -523,16 +518,9 @@ def forward(self, input_step, last_hidden, encoder_outputs): # ``self._SOS_token``. # # -# - Add the ``torch.jit.script_method`` decorator to the ``forward`` -# method -# -# - Adding this decorator lets the JIT compiler know that the function -# that it is decorating should be scripted. -# -# # - Enforce types of ``forward`` method arguments # -# - By default, all parameters to a Torch Script function are assumed +# - By default, all parameters to a TorchScript function are assumed # to be Tensor. If we need to pass an argument of a different type, # we can use function type annotations as introduced in `PEP # 3107 `__. In addition, @@ -553,7 +541,7 @@ def forward(self, input_step, last_hidden, encoder_outputs): # ``self._SOS_token``. # -class GreedySearchDecoder(torch.jit.ScriptModule): +class GreedySearchDecoder(nn.Module): def __init__(self, encoder, decoder, decoder_n_layers): super(GreedySearchDecoder, self).__init__() self.encoder = encoder @@ -564,7 +552,6 @@ def __init__(self, encoder, decoder, decoder_n_layers): __constants__ = ['_device', '_SOS_token', '_decoder_n_layers'] - @torch.jit.script_method def forward(self, input_seq : torch.Tensor, input_length : torch.Tensor, max_length : int): # Forward input through encoder model encoder_outputs, encoder_hidden = self.encoder(input_seq, input_length) @@ -613,7 +600,7 @@ def forward(self, input_seq : torch.Tensor, input_length : torch.Tensor, max_len # an argument, normalizes it, evaluates it, and prints the response. # -def evaluate(encoder, decoder, searcher, voc, sentence, max_length=MAX_LENGTH): +def evaluate(searcher, voc, sentence, max_length=MAX_LENGTH): ### Format input sentence as a batch # words -> indexes indexes_batch = [indexesFromSentence(voc, sentence)] @@ -632,7 +619,7 @@ def evaluate(encoder, decoder, searcher, voc, sentence, max_length=MAX_LENGTH): # Evaluate inputs from user input (stdin) -def evaluateInput(encoder, decoder, searcher, voc): +def evaluateInput(searcher, voc): input_sentence = '' while(1): try: @@ -643,7 +630,7 @@ def evaluateInput(encoder, decoder, searcher, voc): # Normalize sentence input_sentence = normalizeString(input_sentence) # Evaluate sentence - output_words = evaluate(encoder, decoder, searcher, voc, input_sentence) + output_words = evaluate(searcher, voc, input_sentence) # Format and print response sentence output_words[:] = [x for x in output_words if not (x == 'EOS' or x == 'PAD')] print('Bot:', ' '.join(output_words)) @@ -652,12 +639,12 @@ def evaluateInput(encoder, decoder, searcher, voc): print("Error: Encountered unknown word.") # Normalize input sentence and call evaluate() -def evaluateExample(sentence, encoder, decoder, searcher, voc): +def evaluateExample(sentence, searcher, voc): print("> " + sentence) # Normalize sentence input_sentence = normalizeString(sentence) # Evaluate sentence - output_words = evaluate(encoder, decoder, searcher, voc, input_sentence) + output_words = evaluate(searcher, voc, input_sentence) output_words[:] = [x for x in output_words if not (x == 'EOS' or x == 'PAD')] print('Bot:', ' '.join(output_words)) @@ -700,14 +687,17 @@ def evaluateExample(sentence, encoder, decoder, searcher, voc): # ``checkpoint = torch.load(loadFilename, map_location=torch.device('cpu'))`` # line. # -# Hybrid Frontend Notes: +# TorchScript Notes: # ~~~~~~~~~~~~~~~~~~~~~~ # # Notice that we initialize and load parameters into our encoder and -# decoder models as usual. Also, we must call ``.to(device)`` to set the -# device options of the models and ``.eval()`` to set the dropout layers -# to test mode **before** we trace the models. ``TracedModule`` objects do -# not inherit the ``to`` or ``eval`` methods. +# decoder models as usual. If you are using tracing mode(`torch.jit.trace`) +# for some part of your models, you must call .to(device) to set the device +# options of the models and .eval() to set the dropout layers to test mode +# **before** tracing the models. `TracedModule` objects do not inherit the +# ``to`` or ``eval`` methods. Since in this tutorial we are only using +# scripting instead of tracing, we only need to do this before we do +# evaluation (which is the same as we normally do in eager mode). # save_dir = os.path.join("data", "save") @@ -766,16 +756,14 @@ def evaluateExample(sentence, encoder, decoder, searcher, voc): ###################################################################### -# Convert Model to Torch Script +# Convert Model to TorchScript # ----------------------------- # # Encoder # ~~~~~~~ # -# As previously mentioned, to convert the encoder model to Torch Script, -# we use **tracing**. Tracing any module requires running an example input -# through the model’s ``forward`` method and trace the computational graph -# that the data encounters. The encoder model takes an input sequence and +# As previously mentioned, to convert the encoder model to TorchScript, +# we use **scripting**. The encoder model takes an input sequence and # a corresponding lengths tensor. Therefore, we create an example input # sequence tensor ``test_seq``, which is of appropriate size (MAX_LENGTH, # 1), contains numbers in the appropriate range @@ -803,13 +791,13 @@ def evaluateExample(sentence, encoder, decoder, searcher, voc): # ~~~~~~~~~~~~~~~~~~~ # # Recall that we scripted our searcher module due to the presence of -# data-dependent control flow. In the case of scripting, we do the -# conversion work up front by adding the decorator and making sure the -# implementation complies with scripting rules. We initialize the scripted -# searcher the same way that we would initialize an un-scripted variant. +# data-dependent control flow. In the case of scripting, we do necessary +# language changes to make sure the implementation complies with +# TorchScript. We initialize the scripted searcher the same way that we +# would initialize an un-scripted variant. # -### Convert encoder model +### Compile the whole greedy search model to TorchScript model # Create artificial inputs test_seq = torch.LongTensor(MAX_LENGTH, 1).random_(0, voc.num_words).to(device) test_seq_length = torch.LongTensor([test_seq.size()[0]]).to(device) @@ -824,19 +812,21 @@ def evaluateExample(sentence, encoder, decoder, searcher, voc): # Trace the model traced_decoder = torch.jit.trace(decoder, (test_decoder_input, test_decoder_hidden, test_encoder_outputs)) -### Initialize searcher module -scripted_searcher = GreedySearchDecoder(traced_encoder, traced_decoder, decoder.n_layers) +### Initialize searcher module by wrapping ``torch.jit.script`` call +scripted_searcher = torch.jit.script(GreedySearchDecoder(traced_encoder, traced_decoder, decoder.n_layers)) + + ###################################################################### # Print Graphs # ------------ # -# Now that our models are in Torch Script form, we can print the graphs of +# Now that our models are in TorchScript form, we can print the graphs of # each to ensure that we captured the computational graph appropriately. -# Since our ``scripted_searcher`` contains our ``traced_encoder`` and -# ``traced_decoder``, these graphs will print inline. -# +# Since TorchScript allow us to recursively compile the whole model +# hierarchy and inline the ``encoder`` and ``decoder`` graph into a single +# graph, we just need to print the `scripted_searcher` graph print('scripted_searcher graph:\n', scripted_searcher.graph) @@ -845,19 +835,25 @@ def evaluateExample(sentence, encoder, decoder, searcher, voc): # Run Evaluation # -------------- # -# Finally, we will run evaluation of the chatbot model using the Torch -# Script models. If converted correctly, the models will behave exactly as -# they would in their eager-mode representation. +# Finally, we will run evaluation of the chatbot model using the TorchScript +# models. If converted correctly, the models will behave exactly as they +# would in their eager-mode representation. # # By default, we evaluate a few common query sentences. If you want to # chat with the bot yourself, uncomment the ``evaluateInput`` line and # give it a spin. # + +# Use appropriate device +scripted_searcher.to(device) +# Set dropout layers to eval mode +scripted_searcher.eval() + # Evaluate examples sentences = ["hello", "what's up?", "who are you?", "where am I?", "where are you from?"] for s in sentences: - evaluateExample(s, traced_encoder, traced_decoder, scripted_searcher, voc) + evaluateExample(s, scripted_searcher, voc) # Evaluate your input #evaluateInput(traced_encoder, traced_decoder, scripted_searcher, voc) @@ -867,7 +863,7 @@ def evaluateExample(sentence, encoder, decoder, searcher, voc): # Save Model # ---------- # -# Now that we have successfully converted our model to Torch Script, we +# Now that we have successfully converted our model to TorchScript, we # will serialize it for use in a non-Python deployment environment. To do # this, we can simply save our ``scripted_searcher`` module, as this is # the user-facing interface for running inference against the chatbot diff --git a/beginner_source/ptcheat.rst b/beginner_source/ptcheat.rst index 00263069d43..d1360b5b86c 100644 --- a/beginner_source/ptcheat.rst +++ b/beginner_source/ptcheat.rst @@ -29,7 +29,7 @@ See `autograd `__, `functional `__ and `optim `__ -Hybrid frontend +Torchscript and JIT --------------- .. code-block:: python @@ -41,7 +41,7 @@ Hybrid frontend @script # decorator used to indicate data-dependent # control flow within the code being traced -See `hybrid frontend `__ +See `Torchscript `__ ONNX ---- diff --git a/index.rst b/index.rst index d6671f42da3..ce9de2ef007 100644 --- a/index.rst +++ b/index.rst @@ -48,11 +48,6 @@ Getting Started :tooltip: In transfer learning, a model created from one task is used in another :description: :doc:`beginner/transfer_learning_tutorial` -.. customgalleryitem:: - :figure: /_static/img/hybrid.png - :tooltip: Experiment with some of the key features of the PyTorch hybrid frontend - :description: :doc:`beginner/deploy_seq2seq_hybrid_frontend_tutorial` - .. customgalleryitem:: :figure: /_static/img/thumbnails/floppy.png :tooltip: Explore use cases for the saving and loading of PyTorch models @@ -101,8 +96,8 @@ Image .. customgalleryitem:: :figure: /_static/img/cat.jpg - :tooltip: Use ONNX to convert a model defined in PyTorch into the ONNX format and then load it into Caffe2 - :description: :doc:`advanced/super_resolution_with_caffe2` + :tooltip: Exporting a Model from PyTorch to ONNX and Running it using ONNXRuntime + :description: :doc:`advanced/super_resolution_with_onnxruntime` .. raw:: html @@ -201,15 +196,19 @@ Production Usage ---------------------- .. customgalleryitem:: - :tooltip: Loading a PyTorch model in C++ - :description: :doc:`advanced/cpp_export` - :figure: _static/img/cpp_logo.png + :tooltip: Introduction to TorchScript + :description: :doc:`beginner/Intro_to_TorchScript` + :figure: _static/img/torchscript.png .. customgalleryitem:: - :tooltip: Convert a neural style transfer model that has been exported from PyTorch into the Apple CoreML format using ONNX - :description: :doc:`advanced/ONNXLive` - :figure: _static/img/ONNXLive.png + :figure: /_static/img/torchscript.png + :tooltip: Experiment with some of the key features of the TorchScript + :description: :doc:`beginner/deploy_seq2seq_hybrid_frontend_tutorial` +.. customgalleryitem:: + :tooltip: Loading a PyTorch model in C++ + :description: :doc:`advanced/cpp_export` + :figure: _static/img/cpp_logo.png .. customgalleryitem:: :tooltip: Parallelize computations across processes and clusters of machines @@ -231,6 +230,10 @@ Production Usage :description: :doc:`/beginner/aws_distributed_training_tutorial` :figure: _static/img/distributed/DistPyTorch.jpg +.. customgalleryitem:: + :tooltip: Deploying PyTorch and Building a REST API using Flask + :description: :doc:`/intermediate/flask_rest_api_tutorial` + :figure: _static/img/flask.png .. raw:: html @@ -277,7 +280,7 @@ PyTorch in Other Languages intermediate/spatial_transformer_tutorial advanced/neural_style_tutorial beginner/fgsm_tutorial - advanced/super_resolution_with_caffe2 + advanced/super_resolution_with_onnxruntime .. toctree:: :maxdepth: 2 @@ -332,8 +335,8 @@ PyTorch in Other Languages intermediate/model_parallel_tutorial intermediate/ddp_tutorial intermediate/dist_tuto + intermediate/flask_rest_api_tutorial beginner/aws_distributed_training_tutorial - advanced/ONNXLive advanced/cpp_export .. toctree:: diff --git a/intermediate_source/README.txt b/intermediate_source/README.txt index 1b8e637094e..1f3354ad254 100644 --- a/intermediate_source/README.txt +++ b/intermediate_source/README.txt @@ -24,3 +24,7 @@ Intermediate tutorials 6. spatial_transformer_tutorial Spatial Transformer Networks Tutorial https://pytorch.org/tutorials/intermediate/spatial_transformer_tutorial.html + +7. flask_rest_api_tutorial.py + Deploying PyTorch and Building a REST API using Flask + https://pytorch.org/tutorials/beginner/flask_rest_api_tutorial.html diff --git a/intermediate_source/flask_rest_api_tutorial.py b/intermediate_source/flask_rest_api_tutorial.py new file mode 100644 index 00000000000..34b7d033a64 --- /dev/null +++ b/intermediate_source/flask_rest_api_tutorial.py @@ -0,0 +1,352 @@ +# -*- coding: utf-8 -*- +""" +Deploying PyTorch and Building a REST API using Flask +===================================================== +**Author**: `Avinash Sajjanshetty `_ + + +In this tutorial, we will deploy a PyTorch model using Flask and expose a +REST API for model inference. In particular, we will deploy a pretrained +DenseNet 121 model which detects the image. + +.. tip:: All the code used here is released under MIT license and is available on `Github `_. + +""" + + +###################################################################### +# API Definition +# -------------- +# +# We will first define our API endpoints, the request and response types. Our +# API endpoint will be at ``/predict`` which takes HTTP POST requests with a +# ``file`` parameter which contains the image. The response will be of JSON +# response containing the prediction: +# +# :: +# +# {"class_id": "n02124075", "class_name": "Egyptian_cat"} +# +# + +###################################################################### +# Dependencies +# ------------ +# +# Install the required dependenices by running the following command: +# +# :: +# +# $ pip install Flask==1.0.3 torchvision-0.3.0 + + +###################################################################### +# Simple Web Server +# ----------------- +# +# Following is a simple webserver, taken from Flask's documentaion + + +from flask import Flask +app = Flask(__name__) + + +@app.route('/') +def hello(): + return 'Hello World!' + +############################################################################### +# Save the above snippet in a file called ``app.py`` and you can now run a +# Flask development server by typing: +# +# :: +# +# $ FLASK_ENV=development FLASK_APP=app.py flask run + +############################################################################### +# When you visit ``http://localhost:5000/`` in your web browser, you will be +# greeted with ``Hello World!`` text + +############################################################################### +# We will make slight changes to the above snippet, so that it suits our API +# definition. First, we will rename the method to ``predict``. We will update +# the endpoint path to ``/predict``. Since the image files will be sent via +# HTTP POST requests, we will update it so that it also accepts only POST +# requests: + + +@app.route('/predict', methods=['POST']) +def predict(): + return 'Hello World!' + +############################################################################### +# We will also change the response type, so that it returns a JSON response +# containing ImageNet class id and name. The updated ``app.py`` file will +# be now: + +from flask import Flask, jsonify +app = Flask(__name__) + +@app.route('/predict', methods=['POST']) +def predict(): + return jsonify({'class_id': 'IMAGE_NET_XXX', 'class_name': 'Cat'}) + + +###################################################################### +# Inference +# ----------------- +# +# In the next sections we will focus on writing the inference code. This will +# involve two parts, one where we prepare the image so that it can be fed +# to DenseNet and next, we will write the code to get the actual prediction +# from the model. +# +# Preparing the image +# ~~~~~~~~~~~~~~~~~~~ +# +# DenseNet model requires the image to be of 3 channel RGB image of size +# 224 x 224. We will also normalise the image tensor with the required mean +# and standard deviation values. You can read more about it +# `here `_. +# +# We will use ``transforms`` from ``torchvision`` library and build a +# transform pipeline, which transforms our images as required. You +# can read more about transforms `here `_. + +import io + +import torchvision.transforms as transforms +from PIL import Image + +def transform_image(image_bytes): + my_transforms = transforms.Compose([transforms.Resize(255), + transforms.CenterCrop(224), + transforms.ToTensor(), + transforms.Normalize( + [0.485, 0.456, 0.406], + [0.229, 0.224, 0.225])]) + image = Image.open(io.BytesIO(image_bytes)) + return my_transforms(image).unsqueeze(0) + + +###################################################################### +# The above method takes image data in bytes, applies the series of transforms +# and returns a tensor. To test the above method, read an image file in +# bytes mode (first replacing `../_static/img/sample_file.jpeg` with the actual +# path to the file on your computer) and see if you get a tensor back: + +with open("../_static/img/sample_file.jpeg", 'rb') as f: + image_bytes = f.read() + tensor = transform_image(image_bytes=image_bytes) + print(tensor) + +###################################################################### +# Prediction +# ~~~~~~~~~~~~~~~~~~~ +# +# Now will use a pretrained DenseNet 121 model to predict the image class. We +# will use one from ``torchvision`` library, load the model and get an +# inference. While we'll be using a pretrained model in this example, you can +# use this same approach for your own models. See more about loading your +# models in this :doc:`tutorial `. + +from torchvision import models + +# Make sure to pass `pretrained` as `True` to use the pretrained weights: +model = models.densenet121(pretrained=True) +# Since we are using our model only for inference, switch to `eval` mode: +model.eval() + + +def get_prediction(image_bytes): + tensor = transform_image(image_bytes=image_bytes) + outputs = model.forward(tensor) + _, y_hat = outputs.max(1) + return y_hat + + +###################################################################### +# The tensor ``y_hat`` will contain the index of the predicted class id. +# However, we need a human readable class name. For that we need a class id +# to name mapping. Download +# `this file `_ +# as ``imagenet_class_index.json`` and remember where you saved it (or, if you +# are following the exact steps in this tutorial, save it in +# `tutorials/_static`). This file contains the mapping of ImageNet class id to +# ImageNet class name. We will load this JSON file and get the class name of +# the predicted index. + +import json + +imagenet_class_index = json.load(open('../_static/imagenet_class_index.json')) + +def get_prediction(image_bytes): + tensor = transform_image(image_bytes=image_bytes) + outputs = model.forward(tensor) + _, y_hat = outputs.max(1) + predicted_idx = str(y_hat.item()) + return imagenet_class_index[predicted_idx] + + +###################################################################### +# Before using ``imagenet_class_index`` dictionary, first we will convert +# tensor value to a string value, since the keys in the +# ``imagenet_class_index`` dictionary are strings. +# We will test our above method: + + +with open("../_static/img/sample_file.jpeg", 'rb') as f: + image_bytes = f.read() + print(get_prediction(image_bytes=image_bytes)) + +###################################################################### +# You should get a response like this: + +['n02124075', 'Egyptian_cat'] + +###################################################################### +# The first item in array is ImageNet class id and second item is the human +# readable name. +# +# .. Note :: +# Did you notice that ``model`` variable is not part of ``get_prediction`` +# method? Or why is model a global variable? Loading a model can be an +# expensive operation in terms of memory and compute. If we loaded the model in the +# ``get_prediction`` method, then it would get unnecessarily loaded every +# time the method is called. Since, we are building a web server, there +# could be thousands of requests per second, we should not waste time +# redundantly loading the model for every inference. So, we keep the model +# loaded in memory just once. In +# production systems, it's necessary to be efficient about your use of +# compute to be able to serve requests at scale, so you should generally +# load your model before serving requests. + +###################################################################### +# Integrating the model in our API Server +# --------------------------------------- +# +# In this final part we will add our model to our Flask API server. Since +# our API server is supposed to take an image file, we will update our ``predict`` +# method to read files from the requests: +# +# .. code-block:: python +# +# from flask import request +# +# @app.route('/predict', methods=['POST']) +# def predict(): +# if request.method == 'POST': +# # we will get the file from the request +# file = request.files['file'] +# # convert that to bytes +# img_bytes = file.read() +# class_id, class_name = get_prediction(image_bytes=img_bytes) +# return jsonify({'class_id': class_id, 'class_name': class_name}) + +###################################################################### +# The ``app.py`` file is now complete. Following is the full version; replace +# the paths with the paths where you saved your files and it should run: +# +# .. code-block:: python +# +# import io +# import json +# +# from torchvision import models +# import torchvision.transforms as transforms +# from PIL import Image +# from flask import Flask, jsonify, request +# +# +# app = Flask(__name__) +# imagenet_class_index = json.load(open('/imagenet_class_index.json')) +# model = models.densenet121(pretrained=True) +# model.eval() +# +# +# def transform_image(image_bytes): +# my_transforms = transforms.Compose([transforms.Resize(255), +# transforms.CenterCrop(224), +# transforms.ToTensor(), +# transforms.Normalize( +# [0.485, 0.456, 0.406], +# [0.229, 0.224, 0.225])]) +# image = Image.open(io.BytesIO(image_bytes)) +# return my_transforms(image).unsqueeze(0) +# +# +# def get_prediction(image_bytes): +# tensor = transform_image(image_bytes=image_bytes) +# outputs = model.forward(tensor) +# _, y_hat = outputs.max(1) +# predicted_idx = str(y_hat.item()) +# return imagenet_class_index[predicted_idx] +# +# +# @app.route('/predict', methods=['POST']) +# def predict(): +# if request.method == 'POST': +# file = request.files['file'] +# img_bytes = file.read() +# class_id, class_name = get_prediction(image_bytes=img_bytes) +# return jsonify({'class_id': class_id, 'class_name': class_name}) +# +# +# if __name__ == '__main__': +# app.run() + +###################################################################### +# Let's test our web server! Run: +# +# :: +# +# $ FLASK_ENV=development FLASK_APP=app.py flask run + +####################################################################### +# We can use the +# `requests `_ +# library to send a POST request to our app: +# +# .. code-block:: python +# +# import requests +# +# resp = requests.post("http://localhost:5000/predict", +# files={"file": open('/cat.jpg','rb')}) + +####################################################################### +# Printing `resp.json()` will now show the following: +# +# :: +# +# {"class_id": "n02124075", "class_name": "Egyptian_cat"} +# + +###################################################################### +# Next steps +# -------------- +# +# The server we wrote is quite trivial and and may not do everything +# you need for your production application. So, here are some things you +# can do to make it better: +# +# - The endpoint ``/predict`` assumes that always there will be a image file +# in the request. This may not hold true for all requests. Our user may +# send image with a different parameter or send no images at all. +# +# - The user may send non-image type files too. Since we are not handling +# errors, this will break our server. Adding an explicit error handing +# path that will throw an exception would allow us to better handle +# the bad inputs +# +# - Even though the model can recognize a large number of classes of images, +# it may not be able to recognize all images. Enhance the implementation +# to handle cases when the model does not recognize anything in the image. +# +# - We run the Flask server in the development mode, which is not suitable for +# deploying in production. You can check out `this tutorial `_ +# for deploying a Flask server in production. +# +# - You can also add a UI by creating a page with a form which takes the image and +# displays the prediction. Check out the `demo `_ +# of a similar project and its `source code `_. diff --git a/intermediate_source/seq2seq_translation_tutorial.py b/intermediate_source/seq2seq_translation_tutorial.py index 587d5edfe94..fe233e59343 100644 --- a/intermediate_source/seq2seq_translation_tutorial.py +++ b/intermediate_source/seq2seq_translation_tutorial.py @@ -521,7 +521,7 @@ def tensorsFromPair(pair): # each next input, instead of using the decoder's guess as the next input. # Using teacher forcing causes it to converge faster but `when the trained # network is exploited, it may exhibit -# instability `__. +# instability `__. # # You can observe outputs of teacher-forced networks that read with # coherent grammar but wander far from the correct translation - diff --git a/intermediate_source/torchvision_tutorial.rst b/intermediate_source/torchvision_tutorial.rst index 0975f8dda1d..7e700f9bd1b 100644 --- a/intermediate_source/torchvision_tutorial.rst +++ b/intermediate_source/torchvision_tutorial.rst @@ -1,4 +1,4 @@ -TorchVision 0.3 Object Detection Finetuning Tutorial +TorchVision Object Detection Finetuning Tutorial ==================================================== .. tip:: diff --git a/requirements.txt b/requirements.txt index 7d74d4d845e..9a43cc1a632 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # Refer to ./jenkins/build.sh for tutorial build instructions sphinx -sphinx-gallery +sphinx-gallery==0.3.1 tqdm numpy matplotlib @@ -10,6 +10,7 @@ torchvision PyHamcrest bs4 awscli==1.16.35 +flask # PyTorch Theme -e git+git://github.com/pytorch/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme