├── .gitignore ├── LICENSE.txt ├── README.md ├── crnn ├── __init__.py ├── dataset.py ├── keys.py ├── models │ ├── __init__.py │ ├── efficient_densecrnn.py │ └── utils.py ├── test.py └── util.py ├── requirements.txt └── train ├── create-dataset.sh ├── create_dataset └── create_dataset.py ├── pytorch-train ├── dataset.py ├── keys.py ├── models │ ├── __init__.py │ ├── efficient_densecrnn.py │ └── utils.py ├── train.py └── utils.py ├── tensorboard_log.sh └── train-pytorch.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.caffemodel 3 | .#* 4 | .DS_Store 5 | *.whl 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | env/ 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | #lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *,cover 53 | .hypothesis/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # pyenv 80 | .python-version 81 | 82 | # celery beat schedule file 83 | celerybeat-schedule 84 | 85 | # dotenv 86 | .env 87 | 88 | # virtualenv 89 | .venv/ 90 | venv/ 91 | ENV/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | 96 | # Rope project settings 97 | .ropeproject 98 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2018] [phybrain] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # efficientdensenet_crnn 2 | memory efficient densenet+lstm+ctc实现中文识别 3 | 4 | 1.create_dataset 5 | 训练数据转换为lmdb 6 | 7 | 2.train-pytorch.sh 8 | 训练 9 | 10 | 3.tensorboard_log.sh 11 | 训练可视化 12 | 13 | pytorch<0.2 14 | tensorboardX 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ref:chineseocr,memory efficient densenet 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /crnn/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phybrain/efficientdensenet_crnn/a6445c79c45ab11adf895795cd17d58e2ff5bfc2/crnn/__init__.py -------------------------------------------------------------------------------- /crnn/dataset.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # encoding: utf-8 3 | 4 | import random 5 | import torch 6 | from torch.utils.data import Dataset 7 | from torch.utils.data import sampler 8 | import torchvision.transforms as transforms 9 | import lmdb 10 | import six 11 | import sys 12 | from PIL import Image 13 | import numpy as np 14 | 15 | 16 | class lmdbDataset(Dataset): 17 | 18 | def __init__(self, root=None, transform=None, target_transform=None): 19 | self.env = lmdb.open( 20 | root, 21 | max_readers=1, 22 | readonly=True, 23 | lock=False, 24 | readahead=False, 25 | meminit=False) 26 | 27 | if not self.env: 28 | print('cannot creat lmdb from %s' % (root)) 29 | sys.exit(0) 30 | 31 | with self.env.begin(write=False) as txn: 32 | nSamples = int(txn.get('num-samples')) 33 | self.nSamples = nSamples 34 | 35 | self.transform = transform 36 | self.target_transform = target_transform 37 | 38 | def __len__(self): 39 | return self.nSamples 40 | 41 | def __getitem__(self, index): 42 | assert index <= len(self), 'index range error' 43 | index += 1 44 | with self.env.begin(write=False) as txn: 45 | img_key = 'image-%09d' % index 46 | imgbuf = txn.get(img_key) 47 | 48 | buf = six.BytesIO() 49 | buf.write(imgbuf) 50 | buf.seek(0) 51 | try: 52 | img = Image.open(buf).convert('L') 53 | except IOError: 54 | print('Corrupted image for %d' % index) 55 | return self[index + 1] 56 | 57 | if self.transform is not None: 58 | img = self.transform(img) 59 | 60 | label_key = 'label-%09d' % index 61 | label = str(txn.get(label_key)) 62 | if self.target_transform is not None: 63 | label = self.target_transform(label) 64 | 65 | return (img, label) 66 | 67 | 68 | class resizeNormalize(object): 69 | 70 | def __init__(self, size, interpolation=Image.BILINEAR): 71 | self.size = size 72 | self.interpolation = interpolation 73 | self.toTensor = transforms.ToTensor() 74 | 75 | def __call__(self, img): 76 | img = img.resize(self.size, self.interpolation) 77 | img = self.toTensor(img) 78 | img.sub_(0.5).div_(0.5) 79 | return img 80 | 81 | 82 | class randomSequentialSampler(sampler.Sampler): 83 | 84 | def __init__(self, data_source, batch_size): 85 | self.num_samples = len(data_source) 86 | self.batch_size = batch_size 87 | 88 | def __iter__(self): 89 | n_batch = len(self) // self.batch_size 90 | tail = len(self) % self.batch_size 91 | index = torch.LongTensor(len(self)).fill_(0) 92 | for i in range(n_batch): 93 | random_start = random.randint(0, len(self) - self.batch_size) 94 | batch_index = random_start + torch.range(0, self.batch_size - 1) 95 | index[i * self.batch_size:(i + 1) * self.batch_size] = batch_index 96 | # deal with tail 97 | if tail: 98 | random_start = random.randint(0, len(self) - self.batch_size) 99 | tail_index = random_start + torch.range(0, tail - 1) 100 | index[(i + 1) * self.batch_size:] = tail_index 101 | 102 | return iter(index) 103 | 104 | def __len__(self): 105 | return self.num_samples 106 | 107 | 108 | class alignCollate(object): 109 | 110 | def __init__(self, imgH=32, imgW=128, keep_ratio=False, min_ratio=1): 111 | self.imgH = imgH 112 | self.imgW = imgW 113 | self.keep_ratio = keep_ratio 114 | self.min_ratio = min_ratio 115 | 116 | def __call__(self, batch): 117 | images, labels = zip(*batch) 118 | 119 | imgH = self.imgH 120 | imgW = self.imgW 121 | if self.keep_ratio: 122 | ratios = [] 123 | for image in images: 124 | w, h = image.size 125 | ratios.append(w / float(h)) 126 | ratios.sort() 127 | max_ratio = ratios[-1] 128 | imgW = int(np.floor(max_ratio * imgH)) 129 | imgW = max(imgH * self.min_ratio, imgW) # assure imgH >= imgW 130 | 131 | transform = resizeNormalize((imgW, imgH)) 132 | images = [transform(image) for image in images] 133 | images = torch.cat([t.unsqueeze(0) for t in images], 0) 134 | 135 | return images, labels 136 | -------------------------------------------------------------------------------- /crnn/keys.py: -------------------------------------------------------------------------------- 1 | #coding:UTF-8 2 | alphabet = u'\'疗绚诚娇溜题贿者廖更纳加奉公一就汴计与路房原妇208-7其>:],,骑刈全消昏傈安久钟嗅不影处驽蜿资关椤地瘸专问忖票嫉炎韵要月田节陂鄙捌备拳伺眼网盎大傍心东愉汇蹿科每业里航晏字平录先13彤鲶产稍督腴有象岳注绍在泺文定核名水过理让偷率等这发”为含肥酉相鄱七编猥锛日镀蒂掰倒辆栾栗综涩州雌滑馀了机块司宰甙兴矽抚保用沧秩如收息滥页疑埠!!姥异橹钇向下跄的椴沫国绥獠报开民蜇何分凇长讥藏掏施羽中讲派嘟人提浼间世而古多倪唇饯控庚首赛蜓味断制觉技替艰溢潮夕钺外摘枋动双单啮户枇确锦曜杜或能效霜盒然侗电晁放步鹃新杖蜂吒濂瞬评总隍对独合也是府青天诲墙组滴级邀帘示已时骸仄泅和遨店雇疫持巍踮境只亨目鉴崤闲体泄杂作般轰化解迂诿蛭璀腾告版服省师小规程线海办引二桧牌砺洄裴修图痫胡许犊事郛基柴呼食研奶律蛋因葆察戏褒戒再李骁工貂油鹅章啄休场给睡纷豆器捎说敏学会浒设诊格廓查来霓室溆¢诡寥焕舜柒狐回戟砾厄实翩尿五入径惭喹股宇篝|;美期云九祺扮靠锝槌系企酰阊暂蚕忻豁本羹执条钦H獒限进季楦于芘玖铋茯未答粘括样精欠矢甥帷嵩扣令仔风皈行支部蓉刮站蜡救钊汗松嫌成可.鹤院从交政怕活调球局验髌第韫谗串到圆年米/*友忿检区看自敢刃个兹弄流留同没齿星聆轼湖什三建蛔儿椋汕震颧鲤跟力情璺铨陪务指族训滦鄣濮扒商箱十召慷辗所莞管护臭横硒嗓接侦六露党馋驾剖高侬妪幂猗绺骐央酐孝筝课徇缰门男西项句谙瞒秃篇教碲罚声呐景前富嘴鳌稀免朋啬睐去赈鱼住肩愕速旁波厅健茼厥鲟谅投攸炔数方击呋谈绩别愫僚躬鹧胪炳招喇膨泵蹦毛结54谱识陕粽婚拟构且搜任潘比郢妨醪陀桔碘扎选哈骷楷亿明缆脯监睫逻婵共赴淝凡惦及达揖谩澹减焰蛹番祁柏员禄怡峤龙白叽生闯起细装谕竟聚钙上导渊按艾辘挡耒盹饪臀记邮蕙受各医搂普滇朗茸带翻酚(光堤墟蔷万幻〓瑙辈昧盏亘蛀吉铰请子假闻税井诩哨嫂好面琐校馊鬣缂营访炖占农缀否经钚棵趟张亟吏茶谨捻论迸堂玉信吧瞠乡姬寺咬溏苄皿意赉宝尔钰艺特唳踉都荣倚登荐丧奇涵批炭近符傩感道着菊虹仲众懈濯颞眺南释北缝标既茗整撼迤贲挎耱拒某妍卫哇英矶藩治他元领膜遮穗蛾飞荒棺劫么市火温拈棚洼转果奕卸迪伸泳斗邡侄涨屯萋胭氡崮枞惧冒彩斜手豚随旭淑妞形菌吲沱争驯歹挟兆柱传至包内响临红功弩衡寂禁老棍耆渍织害氵渑布载靥嗬虽苹咨娄库雉榜帜嘲套瑚亲簸欧边6腿旮抛吹瞳得镓梗厨继漾愣憨士策窑抑躯襟脏参贸言干绸鳄穷藜音折详)举悍甸癌黎谴死罩迁寒驷袖媒蒋掘模纠恣观祖蛆碍位稿主澧跌筏京锏帝贴证糠才黄鲸略炯饱四出园犀牧容汉杆浈汰瑷造虫瘩怪驴济应花沣谔夙旅价矿以考su呦晒巡茅准肟瓴詹仟褂译桌混宁怦郑抿些余鄂饴攒珑群阖岔琨藓预环洮岌宀杲瀵最常囡周踊女鼓袭喉简范薯遐疏粱黜禧法箔斤遥汝奥直贞撑置绱集她馅逗钧橱魉[恙躁唤9旺膘待脾惫购吗依盲度瘿蠖俾之镗拇鲵厝簧续款展啃表剔品钻腭损清锶统涌寸滨贪链吠冈伎迥咏吁览防迅失汾阔逵绀蔑列川凭努熨揪利俱绉抢鸨我即责膦易毓鹊刹玷岿空嘞绊排术估锷违们苟铜播肘件烫审鲂广像铌惰铟巳胍鲍康憧色恢想拷尤疳知SYFDA峄裕帮握搔氐氘难墒沮雨叁缥悴藐湫娟苑稠颛簇后阕闭蕤缚怎佞码嘤蔡痊舱螯帕赫昵升烬岫、疵蜻髁蕨隶烛械丑盂梁强鲛由拘揉劭龟撤钩呕孛费妻漂求阑崖秤甘通深补赃坎床啪承吼量暇钼烨阂擎脱逮称P神属矗华届狍葑汹育患窒蛰佼静槎运鳗庆逝曼疱克代官此麸耧蚌晟例础榛副测唰缢迹灬霁身岁赭扛又菡乜雾板读陷徉贯郁虑变钓菜圾现琢式乐维渔浜左吾脑钡警T啵拴偌漱湿硕止骼魄积燥联踢玛|则窿见振畿送班钽您赵刨印讨踝籍谡舌崧汽蔽沪酥绒怖财帖肱私莎勋羔霸励哼帐将帅渠纪婴娩岭厘滕吻伤坝冠戊隆瘁介涧物黍并姗奢蹑掣垸锴命箍捉病辖琰眭迩艘绌繁寅若毋思诉类诈燮轲酮狂重反职筱县委磕绣奖晋濉志徽肠呈獐坻口片碰几村柿劳料获亩惕晕厌号罢池正鏖煨家棕复尝懋蜥锅岛扰队坠瘾钬@卧疣镇譬冰彷频黯据垄采八缪瘫型熹砰楠襁箐但嘶绳啤拍盥穆傲洗盯塘怔筛丿台恒喂葛永¥烟酒桦书砂蚝缉态瀚袄圳轻蛛超榧遛姒奘铮右荽望偻卡丶氰附做革索戚坨桷唁垅榻岐偎坛莨山殊微骇陈爨推嗝驹澡藁呤卤嘻糅逛侵郓酌德摇※鬃被慨殡羸昌泡戛鞋河宪沿玲鲨翅哽源铅语照邯址荃佬顺鸳町霭睾瓢夸椁晓酿痈咔侏券噎湍签嚷离午尚社锤背孟使浪缦潍鞅军姹驶笑鳟鲁》孽钜绿洱礴焯椰颖囔乌孔巴互性椽哞聘昨早暮胶炀隧低彗昝铁呓氽藉喔癖瑗姨权胱韦堑蜜酋楝砝毁靓歙锲究屋喳骨辨碑武鸠宫辜烊适坡殃培佩供走蜈迟翼况姣凛浔吃飘债犟金促苛崇坂莳畔绂兵蠕斋根砍亢欢恬崔剁餐榫快扶‖濒缠鳜当彭驭浦篮昀锆秸钳弋娣瞑夷龛苫拱致%嵊障隐弑初娓抉汩累蓖"唬助苓昙押毙破城郧逢嚏獭瞻溱婿赊跨恼璧萃姻貉灵炉密氛陶砸谬衔点琛沛枳层岱诺脍榈埂征冷裁打蹴素瘘逞蛐聊激腱萘踵飒蓟吆取咙簋涓矩曝挺揣座你史舵焱尘苏笈脚溉榨诵樊邓焊义庶儋蟋蒲赦呷杞诠豪还试颓茉太除紫逃痴草充鳕珉祗墨渭烩蘸慕璇镶穴嵘恶骂险绋幕碉肺戳刘潞秣纾潜銮洛须罘销瘪汞兮屉r林厕质探划狸殚善煊烹〒锈逯宸辍泱柚袍远蹋嶙绝峥娥缍雀徵认镱谷=贩勉撩鄯斐洋非祚泾诒饿撬威晷搭芍锥笺蓦候琊档礁沼卵荠忑朝凹瑞头仪弧孵畏铆突衲车浩气茂悖厢枕酝戴湾邹飚攘锂写宵翁岷无喜丈挑嗟绛殉议槽具醇淞笃郴阅饼底壕砚弈询缕庹翟零筷暨舟闺甯撞麂茌蔼很珲捕棠角阉媛娲诽剿尉爵睬韩诰匣危糍镯立浏阳少盆舔擘匪申尬铣旯抖赘瓯居ˇ哮游锭茏歌坏甚秒舞沙仗劲潺阿燧郭嗖霏忠材奂耐跺砀输岖媳氟极摆灿今扔腻枝奎药熄吨话q额慑嘌协喀壳埭视著於愧陲翌峁颅佛腹聋侯咎叟秀颇存较罪哄岗扫栏钾羌己璨枭霉煌涸衿键镝益岢奏连夯睿冥均糖狞蹊稻爸刿胥煜丽肿璃掸跚灾垂樾濑乎莲窄犹撮战馄软络显鸢胸宾妲恕埔蝌份遇巧瞟粒恰剥桡博讯凯堇阶滤卖斌骚彬兑磺樱舷两娱福仃差找桁÷净把阴污戬雷碓蕲楚罡焖抽妫咒仑闱尽邑菁爱贷沥鞑牡嗉崴骤塌嗦订拮滓捡锻次坪杩臃箬融珂鹗宗枚降鸬妯阄堰盐毅必杨崃俺甬状莘货耸菱腼铸唏痤孚澳懒溅翘疙杷淼缙骰喊悉砻坷艇赁界谤纣宴晃茹归饭梢铡街抄肼鬟苯颂撷戈炒咆茭瘙负仰客琉铢封卑珥椿镧窨鬲寿御袤铃萎砖餮脒裳肪孕嫣馗嵇恳氯江石褶冢祸阻狈羞银靳透咳叼敷芷啥它瓤兰痘懊逑肌往捺坊甩呻〃沦忘膻祟菅剧崆智坯臧霍墅攻眯倘拢骠铐庭岙瓠′缺泥迢捶??郏喙掷沌纯秘种听绘固螨团香盗妒埚蓝拖旱荞铀血遏汲辰叩拽幅硬惶桀漠措泼唑齐肾念酱虚屁耶旗砦闵婉馆拭绅韧忏窝醋葺顾辞倜堆辋逆玟贱疾董惘倌锕淘嘀莽俭笏绑鲷杈择蟀粥嗯驰逾案谪褓胫哩昕颚鲢绠躺鹄崂儒俨丝尕泌啊萸彰幺吟骄苣弦脊瑰〈诛镁析闪剪侧哟框螃守嬗燕狭铈缮概迳痧鲲俯售笼痣扉挖满咋援邱扇歪便玑绦峡蛇叨〖泽胃斓喋怂坟猪该蚬炕弥赞棣晔娠挲狡创疖铕镭稷挫弭啾翔粉履苘哦楼秕铂土锣瘟挣栉习享桢袅磨桂谦延坚蔚噗署谟猬钎恐嬉雒倦衅亏璩睹刻殿王算雕麻丘柯骆丸塍谚添鲈垓桎蚯芥予飕镦谌窗醚菀亮搪莺蒿羁足J真轶悬衷靛翊掩哒炅掐冼妮l谐稚荆擒犯陵虏浓崽刍陌傻孜千靖演矜钕煽杰酗渗伞栋俗泫戍罕沾疽灏煦芬磴叱阱榉湃蜀叉醒彪租郡篷屎良垢隗弱陨峪砷掴颁胎雯绵贬沐撵隘篙暖曹陡栓填臼彦瓶琪潼哪鸡摩啦俟锋域耻蔫疯纹撇毒绶痛酯忍爪赳歆嘹辕烈册朴钱吮毯癜娃谀邵厮炽璞邃丐追词瓒忆轧芫谯喷弟半冕裙掖墉绮寝苔势顷褥切衮君佳嫒蚩霞佚洙逊镖暹唛&殒顶碗獗轭铺蛊废恹汨崩珍那杵曲纺夏薰傀闳淬姘舀拧卷楂恍讪厩寮篪赓乘灭盅鞣沟慎挂饺鼾杳树缨丛絮娌臻嗳篡侩述衰矛圈蚜匕筹匿濞晨叶骋郝挚蚴滞增侍描瓣吖嫦蟒匾圣赌毡癞恺百曳需篓肮庖帏卿驿遗蹬鬓骡歉芎胳屐禽烦晌寄媾狄翡苒船廉终痞殇々畦饶改拆悻萄£瓿乃訾桅匮溧拥纱铍骗蕃龋缬父佐疚栎醍掳蓄x惆颜鲆榆〔猎敌暴谥鲫贾罗玻缄扦芪癣落徒臾恿猩托邴肄牵春陛耀刊拓蓓邳堕寇枉淌啡湄兽酷萼碚濠萤夹旬戮梭琥椭昔勺蜊绐晚孺僵宣摄冽旨萌忙蚤眉噼蟑付契瓜悼颡壁曾窕颢澎仿俑浑嵌浣乍碌褪乱蔟隙玩剐葫箫纲围伐决伙漩瑟刑肓镳缓蹭氨皓典畲坍铑檐塑洞倬储胴淳戾吐灼惺妙毕珐缈虱盖羰鸿磅谓髅娴苴唷蚣霹抨贤唠犬誓逍庠逼麓籼釉呜碧秧氩摔霄穸纨辟妈映完牛缴嗷炊恩荔茆掉紊慌莓羟阙萁磐另蕹辱鳐湮吡吩唐睦垠舒圜冗瞿溺芾囱匠僳汐菩饬漓黑霰浸濡窥毂蒡兢驻鹉芮诙迫雳厂忐臆猴鸣蚪栈箕羡渐莆捍眈哓趴蹼埕嚣骛宏淄斑噜严瑛垃椎诱压庾绞焘廿抡迄棘夫纬锹眨瞌侠脐竞瀑孳骧遁姜颦荪滚萦伪逸粳爬锁矣役趣洒颔诏逐奸甭惠攀蹄泛尼拼阮鹰亚颈惑勒〉际肛爷刚钨丰养冶鲽辉蔻画覆皴妊麦返醉皂擀〗酶凑粹悟诀硖港卜z杀涕±舍铠抵弛段敝镐奠拂轴跛袱et沉菇俎薪峦秭蟹历盟菠寡液肢喻染裱悱抱氙赤捅猛跑氮谣仁尺辊窍烙衍架擦倏璐瑁币楞胖夔趸邛惴饕虔蝎§哉贝宽辫炮扩饲籽魏菟锰伍猝末琳哚蛎邂呀姿鄞却歧仙恸椐森牒寤袒婆虢雅钉朵贼欲苞寰故龚坭嘘咫礼硷兀睢汶’铲烧绕诃浃钿哺柜讼颊璁腔洽咐脲簌筠镣玮鞠谁兼姆挥梯蝴谘漕刷躏宦弼b垌劈麟莉揭笙渎仕嗤仓配怏抬错泯镊孰猿邪仍秋鼬壹歇吵炼<尧射柬廷胧霾凳隋肚浮梦祥株堵退L鹫跎凶毽荟炫栩玳甜沂鹿顽伯爹赔蛴徐匡欣狰缸雹蟆疤默沤啜痂衣禅wih辽葳黝钗停沽棒馨颌肉吴硫悯劾娈马啧吊悌镑峭帆瀣涉咸疸滋泣翦拙癸钥蜒+尾庄凝泉婢渴谊乞陆锉糊鸦淮IBN晦弗乔庥葡尻席橡傣渣拿惩麋斛缃矮蛏岘鸽姐膏催奔镒喱蠡摧钯胤柠拐璋鸥卢荡倾^_珀逄萧塾掇贮笆聂圃冲嵬M滔笕值炙偶蜱搐梆汪蔬腑鸯蹇敞绯仨祯谆梧糗鑫啸豺囹猾巢柄瀛筑踌沭暗苁鱿蹉脂蘖牢热木吸溃宠序泞偿拜檩厚朐毗螳吞媚朽担蝗橘畴祈糟盱隼郜惜珠裨铵焙琚唯咚噪骊丫滢勤棉呸咣淀隔蕾窈饨挨煅短匙粕镜赣撕墩酬馁豌颐抗酣氓佑搁哭递耷涡桃贻碣截瘦昭镌蔓氚甲猕蕴蓬散拾纛狼猷铎埋旖矾讳囊糜迈粟蚂紧鲳瘢栽稼羊锄斟睁桥瓮蹙祉醺鼻昱剃跳篱跷蒜翎宅晖嗑壑峻癫屏狠陋袜途憎祀莹滟佶溥臣约盛峰磁慵婪拦莅朕鹦粲裤哎疡嫖琵窟堪谛嘉儡鳝斩郾驸酊妄胜贺徙傅噌钢栅庇恋匝巯邈尸锚粗佟蛟薹纵蚊郅绢锐苗俞篆淆膀鲜煎诶秽寻涮刺怀噶巨褰魅灶灌桉藕谜舸薄搀恽借牯痉渥愿亓耘杠柩锔蚶钣珈喘蹒幽赐稗晤莱泔扯肯菪裆腩豉疆骜腐倭珏唔粮亡润慰伽橄玄誉醐胆龊粼塬陇彼削嗣绾芽妗垭瘴爽薏寨龈泠弹赢漪猫嘧涂恤圭茧烽屑痕巾赖荸凰腮畈亵蹲偃苇澜艮换骺烘苕梓颉肇哗悄氤涠葬屠鹭植竺佯诣鲇瘀鲅邦移滁冯耕癔戌茬沁巩悠湘洪痹锟循谋腕鳃钠捞焉迎碱伫急榷奈邝卯辄皲卟醛畹忧稳雄昼缩阈睑扌耗曦涅捏瞧邕淖漉铝耦禹湛喽莼琅诸苎纂硅始嗨傥燃臂赅嘈呆贵屹壮肋亍蚀卅豹腆邬迭浊}童螂捐圩勐触寞汊壤荫膺渌芳懿遴螈泰蓼蛤茜舅枫朔膝眙避梅判鹜璜牍缅垫藻黔侥惚懂踩腰腈札丞唾慈顿摹荻琬~斧沈滂胁胀幄莜Z匀鄄掌绰茎焚赋萱谑汁铒瞎夺蜗野娆冀弯篁懵灞隽芡脘俐辩芯掺喏膈蝈觐悚踹蔗熠鼠呵抓橼峨畜缔禾崭弃熊摒凸拗穹蒙抒祛劝闫扳阵醌踪喵侣搬仅荧赎蝾琦买婧瞄寓皎冻赝箩莫瞰郊笫姝筒枪遣煸袋舆痱涛母〇启践耙绲盘遂昊搞槿诬纰泓惨檬亻越Co憩熵祷钒暧塔阗胰咄娶魔琶钞邻扬杉殴咽弓〆髻】吭揽霆拄殖脆彻岩芝勃辣剌钝嘎甄佘皖伦授徕憔挪皇庞稔芜踏溴兖卒擢饥鳞煲‰账颗叻斯捧鳍琮讹蛙纽谭酸兔莒睇伟觑羲嗜宜褐旎辛卦诘筋鎏溪挛熔阜晰鳅丢奚灸呱献陉黛鸪甾萨疮拯洲疹辑叙恻谒允柔烂氏逅漆拎惋扈湟纭啕掬擞哥忽涤鸵靡郗瓷扁廊怨雏钮敦E懦憋汀拚啉腌岸f痼瞅尊咀眩飙忌仝迦熬毫胯篑茄腺凄舛碴锵诧羯後漏汤宓仞蚁壶谰皑铄棰罔辅晶苦牟闽\烃饮聿丙蛳朱煤涔鳖犁罐荼砒淦妤黏戎孑婕瑾戢钵枣捋砥衩狙桠稣阎肃梏诫孪昶婊衫嗔侃塞蜃樵峒貌屿欺缫阐栖诟珞荭吝萍嗽恂啻蜴磬峋俸豫谎徊镍韬魇晴U囟猜蛮坐囿伴亭肝佗蝠妃胞滩榴氖垩苋砣扪馏姓轩厉夥侈禀垒岑赏钛辐痔披纸碳“坞蠓挤荥沅悔铧帼蒌蝇apyng哀浆瑶凿桶馈皮奴苜佤伶晗铱炬优弊氢恃甫攥端锌灰稹炝曙邋亥眶碾拉萝绔捷浍腋姑菖凌涞麽锢桨潢绎镰殆锑渝铬困绽觎匈糙暑裹鸟盔肽迷綦『亳佝俘钴觇骥仆疝跪婶郯瀹唉脖踞针晾忒扼瞩叛椒疟嗡邗肆跆玫忡捣咧唆艄蘑潦笛阚沸泻掊菽贫斥髂孢镂赂麝鸾屡衬苷恪叠希粤爻喝茫惬郸绻庸撅碟宄妹膛叮饵崛嗲椅冤搅咕敛尹垦闷蝉霎勰败蓑泸肤鹌幌焦浠鞍刁舰乙竿裔。茵函伊兄丨娜匍謇莪宥似蝽翳酪翠粑薇祢骏赠叫Q噤噻竖芗莠潭俊羿耜O郫趁嗪囚蹶芒洁笋鹑敲硝啶堡渲揩』携宿遒颍扭棱割萜蔸葵琴捂饰衙耿掠募岂窖涟蔺瘤柞瞪怜匹距楔炜哆秦缎幼茁绪痨恨楸娅瓦桩雪嬴伏榔妥铿拌眠雍缇‘卓搓哌觞噩屈哧髓咦巅娑侑淫膳祝勾姊莴胄疃薛蜷胛巷芙芋熙闰勿窃狱剩钏幢陟铛慧靴耍k浙浇飨惟绗祜澈啼咪磷摞诅郦抹跃壬吕肖琏颤尴剡抠凋赚泊津宕殷倔氲漫邺涎怠$垮荬遵俏叹噢饽蜘孙筵疼鞭羧牦箭潴c眸祭髯啖坳愁芩驮倡巽穰沃胚怒凤槛剂趵嫁v邢灯鄢桐睽檗锯槟婷嵋圻诗蕈颠遭痢芸怯馥竭锗徜恭遍籁剑嘱苡龄僧桑潸弘澶楹悲讫愤腥悸谍椹呢桓葭攫阀翰躲敖柑郎笨橇呃魁燎脓葩磋垛玺狮沓砜蕊锺罹蕉翱虐闾巫旦茱嬷枯鹏贡芹汛矫绁拣禺佃讣舫惯乳趋疲挽岚虾衾蠹蹂飓氦铖孩稞瑜壅掀勘妓畅髋W庐牲蓿榕练垣唱邸菲昆婺穿绡麒蚱掂愚泷涪漳妩娉榄讷觅旧藤煮呛柳腓叭庵烷阡罂蜕擂猖咿媲脉【沏貅黠熏哲烁坦酵兜×潇撒剽珩圹乾摸樟帽嗒襄魂轿憬锡〕喃皆咖隅脸残泮袂鹂珊囤捆咤误徨闹淙芊淋怆囗拨梳渤RG绨蚓婀幡狩麾谢唢裸旌伉纶裂驳砼咛澄樨蹈宙澍倍貔操勇蟠摈砧虬够缁悦藿撸艹摁淹豇虎榭ˉ吱d°喧荀踱侮奋偕饷犍惮坑璎徘宛妆袈倩窦昂荏乖K怅撰鳙牙袁酞X痿琼闸雁趾荚虻涝《杏韭偈烤绫鞘卉症遢蓥诋杭荨匆竣簪辙敕虞丹缭咩黟m淤瑕咂铉硼茨嶂痒畸敬涿粪窘熟叔嫔盾忱裘憾梵赡珙咯娘庙溯胺葱痪摊荷卞乒髦寐铭坩胗枷爆溟嚼羚砬轨惊挠罄竽菏氧浅楣盼枢炸阆杯谏噬淇渺俪秆墓泪跻砌痰垡渡耽釜讶鳎煞呗韶舶绷鹳缜旷铊皱龌檀霖奄槐艳蝶旋哝赶骞蚧腊盈丁`蜚矸蝙睨嚓僻鬼醴夜彝磊笔拔栀糕厦邰纫逭纤眦膊馍躇烯蘼冬诤暄骶哑瘠」臊丕愈咱螺擅跋搏硪谄笠淡嘿骅谧鼎皋姚歼蠢驼耳胬挝涯狗蒽孓犷凉芦箴铤孤嘛坤V茴朦挞尖橙诞搴碇洵浚帚蜍漯柘嚎讽芭荤咻祠秉跖埃吓糯眷馒惹娼鲑嫩讴轮瞥靶褚乏缤宋帧删驱碎扑俩俄偏涣竹噱皙佰渚唧斡#镉刀崎筐佣夭贰肴峙哔艿匐牺镛缘仡嫡劣枸堀梨簿鸭蒸亦稽浴{衢束槲j阁揍疥棋潋聪窜乓睛插冉阪苍搽「蟾螟幸仇樽撂慢跤幔俚淅覃觊溶妖帛侨曰妾泗' 3 | -------------------------------------------------------------------------------- /crnn/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phybrain/efficientdensenet_crnn/a6445c79c45ab11adf895795cd17d58e2ff5bfc2/crnn/models/__init__.py -------------------------------------------------------------------------------- /crnn/models/efficient_densecrnn.py: -------------------------------------------------------------------------------- 1 | import math 2 | import torch 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | from functools import reduce 6 | from operator import mul 7 | from collections import OrderedDict 8 | from torch.autograd import Variable, Function 9 | from torch._thnn import type2backend 10 | from torch.backends import cudnn 11 | import utils 12 | 13 | 14 | 15 | class _SharedAllocation(object): 16 | 17 | def __init__(self, storage): 18 | self.storage = storage 19 | 20 | def type(self, t): 21 | self.storage = self.storage.type(t) 22 | 23 | def type_as(self, obj): 24 | if isinstance(obj, Variable): 25 | self.storage = self.storage.type(obj.data.storage().type()) 26 | elif isinstance(obj, torch._TensorBase): 27 | self.storage = self.storage.type(obj.storage().type()) 28 | else: 29 | self.storage = self.storage.type(obj.type()) 30 | 31 | def resize_(self, size): 32 | if self.storage.size() < size: 33 | self.storage.resize_(size) 34 | return self 35 | 36 | class BidirectionalLSTM(nn.Module): 37 | 38 | def __init__(self, nIn, nHidden, nOut, ngpu): 39 | super(BidirectionalLSTM, self).__init__() 40 | self.ngpu = ngpu 41 | 42 | self.rnn = nn.LSTM(nIn, nHidden, bidirectional=True) 43 | self.embedding = nn.Linear(nHidden * 2, nOut) 44 | 45 | def forward(self, input): 46 | recurrent, _ = utils.data_parallel( 47 | self.rnn, input, self.ngpu) # [T, b, h * 2] 48 | 49 | T, b, h = recurrent.size() 50 | t_rec = recurrent.view(T * b, h) 51 | output = utils.data_parallel( 52 | self.embedding, t_rec, self.ngpu) # [T * b, nOut] 53 | output = output.view(T, b, -1) 54 | 55 | return output 56 | 57 | class _EfficientDensenetBottleneck(nn.Module): 58 | 59 | def __init__(self, shared_allocation_1, shared_allocation_2, num_input_channels, num_output_channels): 60 | 61 | super(_EfficientDensenetBottleneck, self).__init__() 62 | self.shared_allocation_1 = shared_allocation_1 63 | self.shared_allocation_2 = shared_allocation_2 64 | self.num_input_channels = num_input_channels 65 | 66 | self.norm_weight = nn.Parameter(torch.Tensor(num_input_channels)) 67 | self.norm_bias = nn.Parameter(torch.Tensor(num_input_channels)) 68 | self.register_buffer('norm_running_mean', torch.zeros(num_input_channels)) 69 | self.register_buffer('norm_running_var', torch.ones(num_input_channels)) 70 | self.conv_weight = nn.Parameter(torch.Tensor(num_output_channels, num_input_channels, 1, 1)) 71 | self._reset_parameters() 72 | 73 | 74 | def _reset_parameters(self): 75 | self.norm_running_mean.zero_() 76 | self.norm_running_var.fill_(1) 77 | self.norm_weight.data.uniform_() 78 | self.norm_bias.data.zero_() 79 | stdv = 1. / math.sqrt(self.num_input_channels) 80 | self.conv_weight.data.uniform_(-stdv, stdv) 81 | 82 | 83 | def forward(self, inputs): 84 | if isinstance(inputs, Variable): 85 | inputs = [inputs] 86 | fn = _EfficientDensenetBottleneckFn(self.shared_allocation_1, self.shared_allocation_2, 87 | self.norm_running_mean, self.norm_running_var, 88 | stride=1, padding=0, dilation=1, groups=1, 89 | training=self.training, momentum=0.1, eps=1e-5) 90 | return fn(self.norm_weight, self.norm_bias, self.conv_weight, *inputs) 91 | 92 | 93 | class _DenseLayer(nn.Sequential): 94 | def __init__(self, shared_allocation_1, shared_allocation_2, num_input_features, growth_rate, bn_size, drop_rate): 95 | super(_DenseLayer, self).__init__() 96 | self.shared_allocation_1 = shared_allocation_1 97 | self.shared_allocation_2 = shared_allocation_2 98 | self.drop_rate = drop_rate 99 | 100 | self.add_module('bn', _EfficientDensenetBottleneck(shared_allocation_1, shared_allocation_2, 101 | num_input_features, bn_size * growth_rate)) 102 | self.add_module('norm.2', nn.BatchNorm2d(bn_size * growth_rate)), 103 | self.add_module('relu.2', nn.ReLU(inplace=True)), 104 | self.add_module('conv.2', nn.Conv2d(bn_size * growth_rate, growth_rate, 105 | kernel_size=3, stride=1, padding=1, bias=False)) 106 | 107 | def forward(self, x): 108 | if isinstance(x, Variable): 109 | prev_features = [x] 110 | else: 111 | prev_features = x 112 | new_features = super(_DenseLayer, self).forward(prev_features) 113 | if self.drop_rate > 0: 114 | new_features = F.dropout(new_features, p=self.drop_rate, training=self.training) 115 | return new_features 116 | 117 | 118 | class _Transition(nn.Sequential): 119 | def __init__(self, num_input_features, num_output_features): 120 | super(_Transition, self).__init__() 121 | self.add_module('norm', nn.BatchNorm2d(num_input_features)) 122 | self.add_module('relu', nn.ReLU(inplace=True)) 123 | self.add_module('conv', nn.Conv2d(num_input_features, num_output_features, 124 | kernel_size=1, stride=1, bias=False)) 125 | self.add_module('pool', nn.AvgPool2d(kernel_size=2, stride=2)) 126 | 127 | 128 | class _DenseBlock(nn.Container): 129 | def __init__(self, num_layers, num_input_features, bn_size, growth_rate, drop_rate, storage_size=1024): 130 | input_storage_1 = torch.Storage(storage_size) 131 | input_storage_2 = torch.Storage(storage_size) 132 | self.final_num_features = num_input_features + (growth_rate * num_layers) 133 | self.shared_allocation_1 = _SharedAllocation(input_storage_1) 134 | self.shared_allocation_2 = _SharedAllocation(input_storage_2) 135 | 136 | super(_DenseBlock, self).__init__() 137 | for i in range(num_layers): 138 | layer = _DenseLayer(self.shared_allocation_1, self.shared_allocation_2, num_input_features + i * growth_rate, 139 | growth_rate, bn_size, drop_rate) 140 | self.add_module('denselayer%d' % (i + 1), layer) 141 | 142 | 143 | def forward(self, x): 144 | # Update storage type 145 | self.shared_allocation_1.type_as(x) 146 | self.shared_allocation_2.type_as(x) 147 | 148 | # Resize storage 149 | final_size = list(x.size()) 150 | final_size[1] = self.final_num_features 151 | final_storage_size = reduce(mul, final_size, 1) 152 | self.shared_allocation_1.resize_(final_storage_size) 153 | self.shared_allocation_2.resize_(final_storage_size) 154 | 155 | outputs = [x] 156 | for module in self.children(): 157 | outputs.append(module.forward(outputs)) 158 | return torch.cat(outputs, dim=1) 159 | 160 | 161 | class DenseCrnnEfficient(nn.Module): 162 | 163 | def __init__(self, nclass,nh,growth_rate=12, block_config=(16, 16, 16), compression=0.5, 164 | num_init_features=24, bn_size=4, drop_rate=0, 165 | small=True): 166 | 167 | super(DenseCrnnEfficient, self).__init__() 168 | assert 0 < compression <= 1, 'compression of densenet should be between 0 and 1' 169 | # self.avgpool_size = 8 if cifar else 7 170 | self.ngpu=1 171 | # First convolution 172 | if small: 173 | self.features = nn.Sequential(OrderedDict([ 174 | ('conv0', nn.Conv2d(1, num_init_features, kernel_size=3, stride=1, padding=1, bias=False)), 175 | ])) 176 | self.features.add_module('norm0', nn.BatchNorm2d(num_init_features)) 177 | self.features.add_module('relu0', nn.ReLU(inplace=True)) 178 | self.features.add_module('pool0', nn.MaxPool2d(kernel_size=3, stride=2, padding=1, 179 | ceil_mode=False)) 180 | else: 181 | self.features = nn.Sequential(OrderedDict([ 182 | ('conv0', nn.Conv2d(3, num_init_features, kernel_size=7, stride=2, padding=3, bias=False)), 183 | ])) 184 | self.features.add_module('norm0', nn.BatchNorm2d(num_init_features)) 185 | self.features.add_module('relu0', nn.ReLU(inplace=True)) 186 | self.features.add_module('pool0', nn.MaxPool2d(kernel_size=3, stride=2, padding=1, 187 | ceil_mode=False)) 188 | 189 | 190 | # Each denseblock 191 | num_features = num_init_features 192 | for i, num_layers in enumerate(block_config): 193 | block = _DenseBlock(num_layers=num_layers, 194 | num_input_features=num_features, 195 | bn_size=bn_size, growth_rate=growth_rate, 196 | drop_rate=drop_rate) 197 | self.features.add_module('denseblock%d' % (i + 1), block) 198 | num_features = num_features + num_layers * growth_rate 199 | if i != len(block_config) - 1: 200 | trans = _Transition(num_input_features=num_features, 201 | num_output_features=int(num_features 202 | * compression)) 203 | self.features.add_module('transition%d' % (i + 1), trans) 204 | num_features = int(num_features * compression) 205 | 206 | # Final batch norm 207 | self.features.add_module('final pooling', nn.AvgPool2d((2, 2), 208 | (2, 1), 209 | (0, 1))) 210 | self.features.add_module('norm_final', nn.BatchNorm2d(num_features)) 211 | self.features.add_module('relu-end',nn.LeakyReLU(0.2, inplace=True)) 212 | self.rnn = nn.Sequential( 213 | BidirectionalLSTM(num_features, nh, nh, self.ngpu), 214 | BidirectionalLSTM(nh, nh, nclass, self.ngpu) 215 | ) 216 | 217 | 218 | def forward(self, x): 219 | #features = self.features(x) 220 | # out = F.relu(features, inplace=True) 221 | conv = utils.data_parallel(self.features, x, self.ngpu) 222 | # b, c, h, w = conv.size() 223 | # assert h == 1, "the height of conv must be 1" 224 | print conv.size() 225 | conv = conv.squeeze(2) 226 | conv = conv.permute(2, 0, 1) # [w, b, c] 227 | 228 | # rnn features 229 | output = utils.data_parallel(self.rnn, conv, self.ngpu) 230 | 231 | return output 232 | 233 | 234 | 235 | 236 | class _EfficientDensenetBottleneckFn(Function): 237 | 238 | def __init__(self, shared_allocation_1, shared_allocation_2, 239 | running_mean, running_var, 240 | stride=1, padding=0, dilation=1, groups=1, 241 | training=False, momentum=0.1, eps=1e-5): 242 | 243 | self.efficient_cat = _EfficientCat(shared_allocation_1.storage) 244 | self.efficient_batch_norm = _EfficientBatchNorm(shared_allocation_2.storage, running_mean, running_var, 245 | training, momentum, eps) 246 | self.efficient_relu = _EfficientReLU() 247 | self.efficient_conv = _EfficientConv2d(stride, padding, dilation, groups) 248 | 249 | # Buffers to store old versions of bn statistics 250 | self.prev_running_mean = self.efficient_batch_norm.running_mean.new() 251 | self.prev_running_mean.resize_as_(self.efficient_batch_norm.running_mean) 252 | self.prev_running_var = self.efficient_batch_norm.running_var.new() 253 | self.prev_running_var.resize_as_(self.efficient_batch_norm.running_var) 254 | self.curr_running_mean = self.efficient_batch_norm.running_mean.new() 255 | self.curr_running_mean.resize_as_(self.efficient_batch_norm.running_mean) 256 | self.curr_running_var = self.efficient_batch_norm.running_var.new() 257 | self.curr_running_var.resize_as_(self.efficient_batch_norm.running_var) 258 | 259 | 260 | def forward(self, bn_weight, bn_bias, conv_weight, *inputs): 261 | self.prev_running_mean.copy_(self.efficient_batch_norm.running_mean) 262 | self.prev_running_var.copy_(self.efficient_batch_norm.running_var) 263 | 264 | bn_input = self.efficient_cat.forward(*inputs) 265 | bn_output = self.efficient_batch_norm.forward(bn_weight, bn_bias, bn_input) 266 | relu_output = self.efficient_relu.forward(bn_output) 267 | conv_output = self.efficient_conv.forward(conv_weight, None, relu_output) 268 | 269 | self.bn_weight = bn_weight 270 | self.bn_bias = bn_bias 271 | self.conv_weight = conv_weight 272 | self.inputs = inputs 273 | return conv_output 274 | 275 | 276 | def backward(self, grad_output): 277 | # Turn off bn training status, and temporarily reset statistics 278 | training = self.efficient_batch_norm.training 279 | self.curr_running_mean.copy_(self.efficient_batch_norm.running_mean) 280 | self.curr_running_var.copy_(self.efficient_batch_norm.running_var) 281 | # self.efficient_batch_norm.training = False 282 | self.efficient_batch_norm.running_mean.copy_(self.prev_running_mean) 283 | self.efficient_batch_norm.running_var.copy_(self.prev_running_var) 284 | 285 | # Recompute concat and BN 286 | cat_output = self.efficient_cat.forward(*self.inputs) 287 | bn_output = self.efficient_batch_norm.forward(self.bn_weight, self.bn_bias, cat_output) 288 | relu_output = self.efficient_relu.forward(bn_output) 289 | 290 | # Conv backward 291 | conv_weight_grad, _, conv_grad_output = self.efficient_conv.backward( 292 | self.conv_weight, None, relu_output, grad_output) 293 | 294 | # ReLU backward 295 | relu_grad_output = self.efficient_relu.backward(bn_output, conv_grad_output) 296 | 297 | # BN backward 298 | self.efficient_batch_norm.running_mean.copy_(self.curr_running_mean) 299 | self.efficient_batch_norm.running_var.copy_(self.curr_running_var) 300 | bn_weight_grad, bn_bias_grad, bn_grad_output = self.efficient_batch_norm.backward( 301 | self.bn_weight, self.bn_bias, cat_output, relu_grad_output) 302 | 303 | # Input backward 304 | grad_inputs = self.efficient_cat.backward(bn_grad_output) 305 | 306 | # Reset bn training status and statistics 307 | self.efficient_batch_norm.training = training 308 | self.efficient_batch_norm.running_mean.copy_(self.curr_running_mean) 309 | self.efficient_batch_norm.running_var.copy_(self.curr_running_var) 310 | 311 | return tuple([bn_weight_grad, bn_bias_grad, conv_weight_grad] + list(grad_inputs)) 312 | 313 | 314 | # The following helper classes are written similarly to pytorch autogrd functions. 315 | # However, they are designed to work on tensors, not variables, and therefore 316 | # are not functions. 317 | 318 | 319 | class _EfficientBatchNorm(object): 320 | def __init__(self, storage, running_mean, running_var, 321 | training=False, momentum=0.1, eps=1e-5): 322 | self.storage = storage 323 | self.running_mean = running_mean 324 | self.running_var = running_var 325 | self.training = training 326 | self.momentum = momentum 327 | self.eps = eps 328 | 329 | def forward(self, weight, bias, input): 330 | # Assert we're using cudnn 331 | for i in ([weight, bias, input]): 332 | if i is not None and not(cudnn.is_acceptable(i)): 333 | raise Exception('You must be using CUDNN to use _EfficientBatchNorm') 334 | 335 | # Create save variables 336 | self.save_mean = self.running_mean.new() 337 | self.save_mean.resize_as_(self.running_mean) 338 | self.save_var = self.running_var.new() 339 | self.save_var.resize_as_(self.running_var) 340 | 341 | # Do forward pass - store in input variable 342 | res = type(input)(self.storage) 343 | res.resize_as_(input) 344 | torch._C._cudnn_batch_norm_forward( 345 | input, res, weight, bias, self.running_mean, self.running_var, 346 | self.save_mean, self.save_var, self.training, self.momentum, self.eps 347 | ) 348 | 349 | return res 350 | 351 | def recompute_forward(self, weight, bias, input): 352 | # Do forward pass - store in input variable 353 | res = type(input)(self.storage) 354 | res.resize_as_(input) 355 | torch._C._cudnn_batch_norm_forward( 356 | input, res, weight, bias, self.running_mean, self.running_var, 357 | self.save_mean, self.save_var, self.training, self.momentum, self.eps 358 | ) 359 | 360 | return res 361 | 362 | def backward(self, weight, bias, input, grad_output): 363 | # Create grad variables 364 | grad_weight = weight.new() 365 | grad_weight.resize_as_(weight) 366 | grad_bias = bias.new() 367 | grad_bias.resize_as_(bias) 368 | 369 | # Run backwards pass - result stored in grad_output 370 | grad_input = grad_output 371 | torch._C._cudnn_batch_norm_backward( 372 | input, grad_output, grad_input, grad_weight, grad_bias, 373 | weight, self.running_mean, self.running_var, self.save_mean, 374 | self.save_var, self.training, self.eps 375 | ) 376 | 377 | # Unpack grad_output 378 | res = tuple([grad_weight, grad_bias, grad_input]) 379 | return res 380 | 381 | 382 | class _EfficientCat(object): 383 | def __init__(self, storage): 384 | self.storage = storage 385 | 386 | def forward(self, *inputs): 387 | # Get size of new varible 388 | self.all_num_channels = [input.size(1) for input in inputs] 389 | size = list(inputs[0].size()) 390 | for num_channels in self.all_num_channels[1:]: 391 | size[1] += num_channels 392 | 393 | # Create variable, using existing storage 394 | res = type(inputs[0])(self.storage).resize_(size) 395 | torch.cat(inputs, dim=1, out=res) 396 | return res 397 | 398 | def backward(self, grad_output): 399 | # Return a table of tensors pointing to same storage 400 | res = [] 401 | index = 0 402 | for num_channels in self.all_num_channels: 403 | new_index = num_channels + index 404 | res.append(grad_output[:, index:new_index]) 405 | index = new_index 406 | 407 | return tuple(res) 408 | 409 | 410 | class _EfficientReLU(object): 411 | def __init__(self): 412 | pass 413 | 414 | def forward(self, input): 415 | backend = type2backend[type(input)] 416 | output = input 417 | backend.Threshold_updateOutput(backend.library_state, input, output, 0, 0, True) 418 | return output 419 | 420 | def backward(self, input, grad_output): 421 | grad_input = grad_output 422 | grad_input.masked_fill_(input <= 0, 0) 423 | return grad_input 424 | 425 | 426 | class _EfficientConv2d(object): 427 | def __init__(self, stride=1, padding=0, dilation=1, groups=1): 428 | self.stride = stride 429 | self.padding = padding 430 | self.dilation = dilation 431 | self.groups = groups 432 | 433 | def _output_size(self, input, weight): 434 | channels = weight.size(0) 435 | output_size = (input.size(0), channels) 436 | for d in range(input.dim() - 2): 437 | in_size = input.size(d + 2) 438 | pad = self.padding 439 | kernel = self.dilation * (weight.size(d + 2) - 1) + 1 440 | stride = self.stride 441 | output_size += ((in_size + (2 * pad) - kernel) // stride + 1,) 442 | if not all(map(lambda s: s > 0, output_size)): 443 | raise ValueError("convolution input is too small (output would be {})".format( 444 | 'x'.join(map(str, output_size)))) 445 | return output_size 446 | 447 | def forward(self, weight, bias, input): 448 | # Assert we're using cudnn 449 | for i in ([weight, bias, input]): 450 | if i is not None and not(cudnn.is_acceptable(i)): 451 | raise Exception('You must be using CUDNN to use _EfficientBatchNorm') 452 | 453 | res = input.new(*self._output_size(input, weight)) 454 | self._cudnn_info = torch._C._cudnn_convolution_full_forward( 455 | input, weight, bias, res, 456 | (self.padding, self.padding), 457 | (self.stride, self.stride), 458 | (self.dilation, self.dilation), 459 | self.groups, cudnn.benchmark 460 | ) 461 | 462 | return res 463 | 464 | def backward(self, weight, bias, input, grad_output): 465 | grad_input = input.new() 466 | grad_input.resize_as_(input) 467 | torch._C._cudnn_convolution_backward_data( 468 | grad_output, grad_input, weight, self._cudnn_info, 469 | cudnn.benchmark) 470 | 471 | grad_weight = weight.new().resize_as_(weight) 472 | torch._C._cudnn_convolution_backward_filter(grad_output, input, grad_weight, self._cudnn_info, 473 | cudnn.benchmark) 474 | 475 | if bias is not None: 476 | grad_bias = bias.new().resize_as_(bias) 477 | torch._C._cudnn_convolution_backward_bias(grad_output, grad_bias, self._cudnn_info) 478 | else: 479 | grad_bias = None 480 | 481 | return grad_weight, grad_bias, grad_input 482 | -------------------------------------------------------------------------------- /crnn/models/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # encoding: utf-8 3 | 4 | import torch.nn as nn 5 | import torch.nn.parallel 6 | 7 | 8 | def data_parallel(model, input, ngpu): 9 | if isinstance(input.data, torch.cuda.FloatTensor) and ngpu > 1: 10 | output = nn.parallel.data_parallel(model, input, range(ngpu)) 11 | else: 12 | output = model(input) 13 | return output 14 | -------------------------------------------------------------------------------- /crnn/test.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | 3 | import random 4 | import torch 5 | import torch.backends.cudnn as cudnn 6 | import torch.optim as optim 7 | import torch.utils.data 8 | from torch.autograd import Variable 9 | import numpy as np 10 | import os 11 | import util 12 | import dataset 13 | from PIL import Image 14 | import models.crnn as crnn 15 | import keys 16 | alphabet = keys.alphabet 17 | print(len(alphabet)) 18 | raw_input('\ninput:') 19 | converter = util.strLabelConverter(alphabet) 20 | model = crnn.CRNN(32, 1, len(alphabet)+1, 256, 1).cuda() 21 | path = 'netCRNN.pth' 22 | model.load_state_dict(torch.load(path)) 23 | print(model) 24 | 25 | 26 | while 1: 27 | im_name = raw_input("\nplease input file name:") 28 | im_path = "./img/" + im_name 29 | image = Image.open(im_path).convert('L') 30 | scale = image.size[1]*1.0 / 32 31 | w = image.size[0] / scale 32 | w = int(w) 33 | print(w) 34 | 35 | transformer = dataset.resizeNormalize((w, 32)) 36 | image = transformer(image).cuda() 37 | image = image.view(1, *image.size()) 38 | image = Variable(image) 39 | model.eval() 40 | preds = model(image) 41 | _, preds = preds.max(2) 42 | preds = preds.squeeze(2) 43 | preds = preds.transpose(1, 0).contiguous().view(-1) 44 | preds_size = Variable(torch.IntTensor([preds.size(0)])) 45 | raw_pred = converter.decode(preds.data, preds_size.data, raw=True) 46 | sim_pred = converter.decode(preds.data, preds_size.data, raw=False) 47 | print('%-20s => %-20s' % (raw_pred, sim_pred)) 48 | 49 | -------------------------------------------------------------------------------- /crnn/util.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # encoding: utf-8 3 | 4 | import torch 5 | import torch.nn as nn 6 | import collections 7 | 8 | 9 | class strLabelConverter(object): 10 | 11 | def __init__(self, alphabet): 12 | self.alphabet = alphabet + u'-' # for `-1` index 13 | self.dict = {} 14 | for i, char in enumerate(alphabet): 15 | # NOTE: 0 is reserved for 'blank' required by wrap_ctc 16 | self.dict[char] = i + 1 17 | def encode(self, text, depth=0): 18 | """Support batch or single str.""" 19 | length = [] 20 | result=[] 21 | for str in text: 22 | str = unicode(str,"utf8") 23 | length.append(len(str)) 24 | for char in str: 25 | #print(char) 26 | index = self.dict[char] 27 | result.append(index) 28 | text = result 29 | return (torch.IntTensor(text), torch.IntTensor(length)) 30 | 31 | def decode(self, t, length, raw=False): 32 | if length.numel() == 1: 33 | length = length[0] 34 | t = t[:length] 35 | if raw: 36 | return ''.join([self.alphabet[i - 1] for i in t]) 37 | else: 38 | char_list = [] 39 | for i in range(length): 40 | if t[i] != 0 and (not (i > 0 and t[i - 1] == t[i])): 41 | char_list.append(self.alphabet[t[i] - 1]) 42 | return ''.join(char_list) 43 | else: 44 | texts = [] 45 | index = 0 46 | for i in range(length.numel()): 47 | l = length[i] 48 | texts.append(self.decode( 49 | t[index:index + l], torch.IntTensor([l]), raw=raw)) 50 | index += l 51 | return texts 52 | 53 | 54 | class averager(object): 55 | 56 | def __init__(self): 57 | self.reset() 58 | 59 | def add(self, v): 60 | self.n_count += v.data.numel() 61 | # NOTE: not `+= v.sum()`, which will add a node in the compute graph, 62 | # which lead to memory leak 63 | self.sum += v.data.sum() 64 | 65 | def reset(self): 66 | self.n_count = 0 67 | self.sum = 0 68 | 69 | def val(self): 70 | res = 0 71 | if self.n_count != 0: 72 | res = self.sum / float(self.n_count) 73 | return res 74 | 75 | 76 | def oneHot(v, v_length, nc): 77 | batchSize = v_length.size(0) 78 | maxLength = v_length.max() 79 | v_onehot = torch.FloatTensor(batchSize, maxLength, nc).fill_(0) 80 | acc = 0 81 | for i in range(batchSize): 82 | length = v_length[i] 83 | label = v[acc:acc + length].view(-1, 1).long() 84 | v_onehot[i, :length].scatter_(1, label, 1.0) 85 | acc += length 86 | return v_onehot 87 | 88 | 89 | def loadData(v, data): 90 | v.data.resize_(data.size()).copy_(data) 91 | 92 | 93 | def prettyPrint(v): 94 | print('Size {0}, Type: {1}'.format(str(v.size()), v.data.type())) 95 | print('| Max: %f | Min: %f | Mean: %f' % (v.max().data[0], v.min().data[0], v.mean().data[0])) 96 | 97 | 98 | def assureRatio(img): 99 | """Ensure imgH <= imgW.""" 100 | b, c, h, w = img.size() 101 | if h > w: 102 | main = nn.UpsamplingBilinear2d(size=(h, h), scale_factor=None) 103 | img = main(img) 104 | return img 105 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | eventlet==0.20.1 # new version has bug 2 | pytorch==0.2.0 3 | numpy==1.14.2 4 | requests 5 | kombu 6 | easydict 7 | Cython 8 | lmdb 9 | tensorboard_logger 10 | distance -------------------------------------------------------------------------------- /train/create-dataset.sh: -------------------------------------------------------------------------------- 1 | cd create_dataset 2 | python create_dataset.py 3 | -------------------------------------------------------------------------------- /train/create_dataset/create_dataset.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | import os 4 | import lmdb # install lmdb by "pip install lmdb" 5 | import cv2 6 | import numpy as np 7 | #from genLineText import GenTextImage 8 | 9 | def checkImageIsValid(imageBin): 10 | if imageBin is None: 11 | return False 12 | imageBuf = np.fromstring(imageBin, dtype=np.uint8) 13 | img = cv2.imdecode(imageBuf, cv2.IMREAD_GRAYSCALE) 14 | if img is None: 15 | return False 16 | imgH, imgW = img.shape[0], img.shape[1] 17 | if imgH * imgW == 0: 18 | return False 19 | return True 20 | 21 | 22 | def writeCache(env, cache): 23 | with env.begin(write=True) as txn: 24 | for k, v in cache.iteritems(): 25 | txn.put(k, v) 26 | 27 | 28 | def createDataset(outputPath, imagePathList, labelList, lexiconList=None, checkValid=True): 29 | """ 30 | Create LMDB dataset for CRNN training. 31 | 32 | ARGS: 33 | outputPath : LMDB output path 34 | imagePathList : list of image path 35 | labelList : list of corresponding groundtruth texts 36 | lexiconList : (optional) list of lexicon lists 37 | checkValid : if true, check the validity of every image 38 | """ 39 | #print (len(imagePathList) , len(labelList)) 40 | assert(len(imagePathList) == len(labelList)) 41 | nSamples = len(imagePathList) 42 | print '...................' 43 | env = lmdb.open(outputPath, map_size=1099511627776,map_async=True, metasync=False, writemap=True) 44 | 45 | cache = {} 46 | cnt = 1 47 | for i in xrange(nSamples): 48 | imagePath = imagePathList[i] 49 | label = labelList[i] 50 | if not os.path.exists(imagePath): 51 | print('%s does not exist' % imagePath) 52 | continue 53 | with open(imagePath, 'r') as f: 54 | imageBin = f.read() 55 | if checkValid: 56 | if not checkImageIsValid(imageBin): 57 | print('%s is not a valid image' % imagePath) 58 | continue 59 | 60 | imageKey = 'image-%09d' % cnt 61 | labelKey = 'label-%09d' % cnt 62 | cache[imageKey] = imageBin 63 | cache[labelKey] = label 64 | if lexiconList: 65 | lexiconKey = 'lexicon-%09d' % cnt 66 | cache[lexiconKey] = ' '.join(lexiconList[i]) 67 | if cnt % 1000 == 0: 68 | writeCache(env, cache) 69 | cache = {} 70 | print('Written %d / %d' % (cnt, nSamples)) 71 | cnt += 1 72 | nSamples = cnt-1 73 | cache['num-samples'] = str(nSamples) 74 | writeCache(env, cache) 75 | env.sync() 76 | env.close() 77 | print('Created dataset with %d samples' % nSamples) 78 | 79 | 80 | def read_text(path): 81 | 82 | with open(path) as f: 83 | text = f.read() 84 | text = text.strip() 85 | 86 | return text 87 | 88 | 89 | import glob 90 | if __name__ == '__main__': 91 | 92 | imagePathList=[] 93 | imgLabelList=[] 94 | ##lmdb 输出目录 95 | outputPath = '../data/newdata/train' 96 | path='/data/dataset/tpa_num_sogou/imgs/' 97 | txtpath = '/data/dataset/tpa_num_sogou/label.train.txt' 98 | with open(txtpath,'r') as f: 99 | for line in f: 100 | p=line.split(' ') 101 | if os.path.exists(path+p[0]): 102 | print path+p[0] 103 | imagePathList.append(path+p[0]) 104 | p[1]=p[1].split('\n')[0] 105 | imgLabelList.append(p[1]) 106 | print p[1] 107 | else: 108 | continue 109 | 110 | 111 | 112 | 113 | # imgLabelList = sorted(imgLabelLists,key = lambda x:len(x[1])) 114 | # imgPaths = [ p[0] for p in imgLabelList] 115 | # txtLists = [ p[1] for p in imgLabelList] 116 | 117 | createDataset(outputPath, imagePathList, imgLabelList, lexiconList=None, checkValid=True) 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /train/pytorch-train/dataset.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # encoding: utf-8 3 | 4 | import random 5 | import torch 6 | from torch.utils.data import Dataset 7 | from torch.utils.data import sampler 8 | import torchvision.transforms as transforms 9 | import lmdb 10 | import six 11 | import sys 12 | from PIL import Image 13 | import numpy as np 14 | 15 | 16 | class lmdbDataset(Dataset): 17 | 18 | def __init__(self, root=None, transform=None, target_transform=None): 19 | self.env = lmdb.open( 20 | root, 21 | max_readers=1, 22 | readonly=True, 23 | lock=False, 24 | readahead=False, 25 | meminit=False) 26 | 27 | if not self.env: 28 | print('cannot creat lmdb from %s' % (root)) 29 | sys.exit(0) 30 | 31 | with self.env.begin(write=False) as txn: 32 | nSamples = int(txn.get('num-samples')) 33 | self.nSamples = nSamples 34 | 35 | self.transform = transform 36 | self.target_transform = target_transform 37 | 38 | def __len__(self): 39 | return self.nSamples 40 | 41 | def __getitem__(self, index): 42 | assert index <= len(self), 'index range error' 43 | index += 1 44 | with self.env.begin(write=False) as txn: 45 | img_key = 'image-%09d' % index 46 | imgbuf = txn.get(img_key) 47 | 48 | buf = six.BytesIO() 49 | buf.write(imgbuf) 50 | buf.seek(0) 51 | try: 52 | img = Image.open(buf).convert('L') 53 | except IOError: 54 | print('Corrupted image for %d' % index) 55 | return self[index + 1] 56 | 57 | if self.transform is not None: 58 | img = self.transform(img) 59 | 60 | label_key = 'label-%09d' % index 61 | label = str(txn.get(label_key)) 62 | 63 | if self.target_transform is not None: 64 | label = self.target_transform(label) 65 | 66 | return (img, label) 67 | 68 | 69 | class resizeNormalize(object): 70 | 71 | def __init__(self, size, interpolation=Image.BILINEAR): 72 | self.size = size 73 | self.interpolation = interpolation 74 | self.toTensor = transforms.ToTensor() 75 | 76 | def __call__(self, img): 77 | img = img.resize(self.size, self.interpolation) 78 | img = self.toTensor(img) 79 | img.sub_(0.5).div_(0.5) 80 | return img 81 | 82 | 83 | class randomSequentialSampler(sampler.Sampler): 84 | 85 | def __init__(self, data_source, batch_size): 86 | self.num_samples = len(data_source) 87 | self.batch_size = batch_size 88 | 89 | def __iter__(self): 90 | n_batch = len(self) // self.batch_size 91 | tail = len(self) % self.batch_size 92 | index = torch.LongTensor(len(self)).fill_(0) 93 | for i in range(n_batch): 94 | random_start = random.randint(0, len(self) - self.batch_size) 95 | batch_index = random_start + torch.range(0, self.batch_size - 1) 96 | index[i * self.batch_size:(i + 1) * self.batch_size] = batch_index 97 | # deal with tail 98 | if tail: 99 | random_start = random.randint(0, len(self) - self.batch_size) 100 | tail_index = random_start + torch.range(0, tail - 1) 101 | index[(i + 1) * self.batch_size:] = tail_index 102 | 103 | return iter(index) 104 | 105 | def __len__(self): 106 | return self.num_samples 107 | 108 | 109 | class alignCollate(object): 110 | 111 | def __init__(self, imgH=32, imgW=100, keep_ratio=False, min_ratio=1): 112 | self.imgH = imgH 113 | self.imgW = imgW 114 | self.keep_ratio = keep_ratio 115 | self.min_ratio = min_ratio 116 | 117 | def __call__(self, batch): 118 | images, labels = zip(*batch) 119 | 120 | imgH = self.imgH 121 | imgW = self.imgW 122 | if self.keep_ratio: 123 | ratios = [] 124 | for image in images: 125 | w, h = image.size 126 | ratios.append(w / float(h)) 127 | ratios.sort() 128 | max_ratio = ratios[-1] 129 | imgW = int(np.floor(max_ratio * imgH)) 130 | imgW = max(imgH * self.min_ratio, imgW) # assure imgH >= imgW 131 | 132 | transform = resizeNormalize((imgW, imgH)) 133 | images = [transform(image) for image in images] 134 | images = torch.cat([t.unsqueeze(0) for t in images], 0) 135 | 136 | return images, labels 137 | -------------------------------------------------------------------------------- /train/pytorch-train/keys.py: -------------------------------------------------------------------------------- 1 | #coding:UTF-8 2 | alphabet = u'\'疗绚诚娇溜题贿者廖更纳加奉公一就汴计与路房原妇208-7其>:],,骑刈全消昏傈安久钟嗅不影处驽蜿资关椤地瘸专问忖票嫉炎韵要月田节陂鄙捌备拳伺眼网盎大傍心东愉汇蹿科每业里航晏字平录先13彤鲶产稍督腴有象岳注绍在泺文定核名水过理让偷率等这发”为含肥酉相鄱七编猥锛日镀蒂掰倒辆栾栗综涩州雌滑馀了机块司宰甙兴矽抚保用沧秩如收息滥页疑埠!!姥异橹钇向下跄的椴沫国绥獠报开民蜇何分凇长讥藏掏施羽中讲派嘟人提浼间世而古多倪唇饯控庚首赛蜓味断制觉技替艰溢潮夕钺外摘枋动双单啮户枇确锦曜杜或能效霜盒然侗电晁放步鹃新杖蜂吒濂瞬评总隍对独合也是府青天诲墙组滴级邀帘示已时骸仄泅和遨店雇疫持巍踮境只亨目鉴崤闲体泄杂作般轰化解迂诿蛭璀腾告版服省师小规程线海办引二桧牌砺洄裴修图痫胡许犊事郛基柴呼食研奶律蛋因葆察戏褒戒再李骁工貂油鹅章啄休场给睡纷豆器捎说敏学会浒设诊格廓查来霓室溆¢诡寥焕舜柒狐回戟砾厄实翩尿五入径惭喹股宇篝|;美期云九祺扮靠锝槌系企酰阊暂蚕忻豁本羹执条钦H獒限进季楦于芘玖铋茯未答粘括样精欠矢甥帷嵩扣令仔风皈行支部蓉刮站蜡救钊汗松嫌成可.鹤院从交政怕活调球局验髌第韫谗串到圆年米/*友忿检区看自敢刃个兹弄流留同没齿星聆轼湖什三建蛔儿椋汕震颧鲤跟力情璺铨陪务指族训滦鄣濮扒商箱十召慷辗所莞管护臭横硒嗓接侦六露党馋驾剖高侬妪幂猗绺骐央酐孝筝课徇缰门男西项句谙瞒秃篇教碲罚声呐景前富嘴鳌稀免朋啬睐去赈鱼住肩愕速旁波厅健茼厥鲟谅投攸炔数方击呋谈绩别愫僚躬鹧胪炳招喇膨泵蹦毛结54谱识陕粽婚拟构且搜任潘比郢妨醪陀桔碘扎选哈骷楷亿明缆脯监睫逻婵共赴淝凡惦及达揖谩澹减焰蛹番祁柏员禄怡峤龙白叽生闯起细装谕竟聚钙上导渊按艾辘挡耒盹饪臀记邮蕙受各医搂普滇朗茸带翻酚(光堤墟蔷万幻〓瑙辈昧盏亘蛀吉铰请子假闻税井诩哨嫂好面琐校馊鬣缂营访炖占农缀否经钚棵趟张亟吏茶谨捻论迸堂玉信吧瞠乡姬寺咬溏苄皿意赉宝尔钰艺特唳踉都荣倚登荐丧奇涵批炭近符傩感道着菊虹仲众懈濯颞眺南释北缝标既茗整撼迤贲挎耱拒某妍卫哇英矶藩治他元领膜遮穗蛾飞荒棺劫么市火温拈棚洼转果奕卸迪伸泳斗邡侄涨屯萋胭氡崮枞惧冒彩斜手豚随旭淑妞形菌吲沱争驯歹挟兆柱传至包内响临红功弩衡寂禁老棍耆渍织害氵渑布载靥嗬虽苹咨娄库雉榜帜嘲套瑚亲簸欧边6腿旮抛吹瞳得镓梗厨继漾愣憨士策窑抑躯襟脏参贸言干绸鳄穷藜音折详)举悍甸癌黎谴死罩迁寒驷袖媒蒋掘模纠恣观祖蛆碍位稿主澧跌筏京锏帝贴证糠才黄鲸略炯饱四出园犀牧容汉杆浈汰瑷造虫瘩怪驴济应花沣谔夙旅价矿以考su呦晒巡茅准肟瓴詹仟褂译桌混宁怦郑抿些余鄂饴攒珑群阖岔琨藓预环洮岌宀杲瀵最常囡周踊女鼓袭喉简范薯遐疏粱黜禧法箔斤遥汝奥直贞撑置绱集她馅逗钧橱魉[恙躁唤9旺膘待脾惫购吗依盲度瘿蠖俾之镗拇鲵厝簧续款展啃表剔品钻腭损清锶统涌寸滨贪链吠冈伎迥咏吁览防迅失汾阔逵绀蔑列川凭努熨揪利俱绉抢鸨我即责膦易毓鹊刹玷岿空嘞绊排术估锷违们苟铜播肘件烫审鲂广像铌惰铟巳胍鲍康憧色恢想拷尤疳知SYFDA峄裕帮握搔氐氘难墒沮雨叁缥悴藐湫娟苑稠颛簇后阕闭蕤缚怎佞码嘤蔡痊舱螯帕赫昵升烬岫、疵蜻髁蕨隶烛械丑盂梁强鲛由拘揉劭龟撤钩呕孛费妻漂求阑崖秤甘通深补赃坎床啪承吼量暇钼烨阂擎脱逮称P神属矗华届狍葑汹育患窒蛰佼静槎运鳗庆逝曼疱克代官此麸耧蚌晟例础榛副测唰缢迹灬霁身岁赭扛又菡乜雾板读陷徉贯郁虑变钓菜圾现琢式乐维渔浜左吾脑钡警T啵拴偌漱湿硕止骼魄积燥联踢玛|则窿见振畿送班钽您赵刨印讨踝籍谡舌崧汽蔽沪酥绒怖财帖肱私莎勋羔霸励哼帐将帅渠纪婴娩岭厘滕吻伤坝冠戊隆瘁介涧物黍并姗奢蹑掣垸锴命箍捉病辖琰眭迩艘绌繁寅若毋思诉类诈燮轲酮狂重反职筱县委磕绣奖晋濉志徽肠呈獐坻口片碰几村柿劳料获亩惕晕厌号罢池正鏖煨家棕复尝懋蜥锅岛扰队坠瘾钬@卧疣镇譬冰彷频黯据垄采八缪瘫型熹砰楠襁箐但嘶绳啤拍盥穆傲洗盯塘怔筛丿台恒喂葛永¥烟酒桦书砂蚝缉态瀚袄圳轻蛛超榧遛姒奘铮右荽望偻卡丶氰附做革索戚坨桷唁垅榻岐偎坛莨山殊微骇陈爨推嗝驹澡藁呤卤嘻糅逛侵郓酌德摇※鬃被慨殡羸昌泡戛鞋河宪沿玲鲨翅哽源铅语照邯址荃佬顺鸳町霭睾瓢夸椁晓酿痈咔侏券噎湍签嚷离午尚社锤背孟使浪缦潍鞅军姹驶笑鳟鲁》孽钜绿洱礴焯椰颖囔乌孔巴互性椽哞聘昨早暮胶炀隧低彗昝铁呓氽藉喔癖瑗姨权胱韦堑蜜酋楝砝毁靓歙锲究屋喳骨辨碑武鸠宫辜烊适坡殃培佩供走蜈迟翼况姣凛浔吃飘债犟金促苛崇坂莳畔绂兵蠕斋根砍亢欢恬崔剁餐榫快扶‖濒缠鳜当彭驭浦篮昀锆秸钳弋娣瞑夷龛苫拱致%嵊障隐弑初娓抉汩累蓖"唬助苓昙押毙破城郧逢嚏獭瞻溱婿赊跨恼璧萃姻貉灵炉密氛陶砸谬衔点琛沛枳层岱诺脍榈埂征冷裁打蹴素瘘逞蛐聊激腱萘踵飒蓟吆取咙簋涓矩曝挺揣座你史舵焱尘苏笈脚溉榨诵樊邓焊义庶儋蟋蒲赦呷杞诠豪还试颓茉太除紫逃痴草充鳕珉祗墨渭烩蘸慕璇镶穴嵘恶骂险绋幕碉肺戳刘潞秣纾潜銮洛须罘销瘪汞兮屉r林厕质探划狸殚善煊烹〒锈逯宸辍泱柚袍远蹋嶙绝峥娥缍雀徵认镱谷=贩勉撩鄯斐洋非祚泾诒饿撬威晷搭芍锥笺蓦候琊档礁沼卵荠忑朝凹瑞头仪弧孵畏铆突衲车浩气茂悖厢枕酝戴湾邹飚攘锂写宵翁岷无喜丈挑嗟绛殉议槽具醇淞笃郴阅饼底壕砚弈询缕庹翟零筷暨舟闺甯撞麂茌蔼很珲捕棠角阉媛娲诽剿尉爵睬韩诰匣危糍镯立浏阳少盆舔擘匪申尬铣旯抖赘瓯居ˇ哮游锭茏歌坏甚秒舞沙仗劲潺阿燧郭嗖霏忠材奂耐跺砀输岖媳氟极摆灿今扔腻枝奎药熄吨话q额慑嘌协喀壳埭视著於愧陲翌峁颅佛腹聋侯咎叟秀颇存较罪哄岗扫栏钾羌己璨枭霉煌涸衿键镝益岢奏连夯睿冥均糖狞蹊稻爸刿胥煜丽肿璃掸跚灾垂樾濑乎莲窄犹撮战馄软络显鸢胸宾妲恕埔蝌份遇巧瞟粒恰剥桡博讯凯堇阶滤卖斌骚彬兑磺樱舷两娱福仃差找桁÷净把阴污戬雷碓蕲楚罡焖抽妫咒仑闱尽邑菁爱贷沥鞑牡嗉崴骤塌嗦订拮滓捡锻次坪杩臃箬融珂鹗宗枚降鸬妯阄堰盐毅必杨崃俺甬状莘货耸菱腼铸唏痤孚澳懒溅翘疙杷淼缙骰喊悉砻坷艇赁界谤纣宴晃茹归饭梢铡街抄肼鬟苯颂撷戈炒咆茭瘙负仰客琉铢封卑珥椿镧窨鬲寿御袤铃萎砖餮脒裳肪孕嫣馗嵇恳氯江石褶冢祸阻狈羞银靳透咳叼敷芷啥它瓤兰痘懊逑肌往捺坊甩呻〃沦忘膻祟菅剧崆智坯臧霍墅攻眯倘拢骠铐庭岙瓠′缺泥迢捶??郏喙掷沌纯秘种听绘固螨团香盗妒埚蓝拖旱荞铀血遏汲辰叩拽幅硬惶桀漠措泼唑齐肾念酱虚屁耶旗砦闵婉馆拭绅韧忏窝醋葺顾辞倜堆辋逆玟贱疾董惘倌锕淘嘀莽俭笏绑鲷杈择蟀粥嗯驰逾案谪褓胫哩昕颚鲢绠躺鹄崂儒俨丝尕泌啊萸彰幺吟骄苣弦脊瑰〈诛镁析闪剪侧哟框螃守嬗燕狭铈缮概迳痧鲲俯售笼痣扉挖满咋援邱扇歪便玑绦峡蛇叨〖泽胃斓喋怂坟猪该蚬炕弥赞棣晔娠挲狡创疖铕镭稷挫弭啾翔粉履苘哦楼秕铂土锣瘟挣栉习享桢袅磨桂谦延坚蔚噗署谟猬钎恐嬉雒倦衅亏璩睹刻殿王算雕麻丘柯骆丸塍谚添鲈垓桎蚯芥予飕镦谌窗醚菀亮搪莺蒿羁足J真轶悬衷靛翊掩哒炅掐冼妮l谐稚荆擒犯陵虏浓崽刍陌傻孜千靖演矜钕煽杰酗渗伞栋俗泫戍罕沾疽灏煦芬磴叱阱榉湃蜀叉醒彪租郡篷屎良垢隗弱陨峪砷掴颁胎雯绵贬沐撵隘篙暖曹陡栓填臼彦瓶琪潼哪鸡摩啦俟锋域耻蔫疯纹撇毒绶痛酯忍爪赳歆嘹辕烈册朴钱吮毯癜娃谀邵厮炽璞邃丐追词瓒忆轧芫谯喷弟半冕裙掖墉绮寝苔势顷褥切衮君佳嫒蚩霞佚洙逊镖暹唛&殒顶碗獗轭铺蛊废恹汨崩珍那杵曲纺夏薰傀闳淬姘舀拧卷楂恍讪厩寮篪赓乘灭盅鞣沟慎挂饺鼾杳树缨丛絮娌臻嗳篡侩述衰矛圈蚜匕筹匿濞晨叶骋郝挚蚴滞增侍描瓣吖嫦蟒匾圣赌毡癞恺百曳需篓肮庖帏卿驿遗蹬鬓骡歉芎胳屐禽烦晌寄媾狄翡苒船廉终痞殇々畦饶改拆悻萄£瓿乃訾桅匮溧拥纱铍骗蕃龋缬父佐疚栎醍掳蓄x惆颜鲆榆〔猎敌暴谥鲫贾罗玻缄扦芪癣落徒臾恿猩托邴肄牵春陛耀刊拓蓓邳堕寇枉淌啡湄兽酷萼碚濠萤夹旬戮梭琥椭昔勺蜊绐晚孺僵宣摄冽旨萌忙蚤眉噼蟑付契瓜悼颡壁曾窕颢澎仿俑浑嵌浣乍碌褪乱蔟隙玩剐葫箫纲围伐决伙漩瑟刑肓镳缓蹭氨皓典畲坍铑檐塑洞倬储胴淳戾吐灼惺妙毕珐缈虱盖羰鸿磅谓髅娴苴唷蚣霹抨贤唠犬誓逍庠逼麓籼釉呜碧秧氩摔霄穸纨辟妈映完牛缴嗷炊恩荔茆掉紊慌莓羟阙萁磐另蕹辱鳐湮吡吩唐睦垠舒圜冗瞿溺芾囱匠僳汐菩饬漓黑霰浸濡窥毂蒡兢驻鹉芮诙迫雳厂忐臆猴鸣蚪栈箕羡渐莆捍眈哓趴蹼埕嚣骛宏淄斑噜严瑛垃椎诱压庾绞焘廿抡迄棘夫纬锹眨瞌侠脐竞瀑孳骧遁姜颦荪滚萦伪逸粳爬锁矣役趣洒颔诏逐奸甭惠攀蹄泛尼拼阮鹰亚颈惑勒〉际肛爷刚钨丰养冶鲽辉蔻画覆皴妊麦返醉皂擀〗酶凑粹悟诀硖港卜z杀涕±舍铠抵弛段敝镐奠拂轴跛袱et沉菇俎薪峦秭蟹历盟菠寡液肢喻染裱悱抱氙赤捅猛跑氮谣仁尺辊窍烙衍架擦倏璐瑁币楞胖夔趸邛惴饕虔蝎§哉贝宽辫炮扩饲籽魏菟锰伍猝末琳哚蛎邂呀姿鄞却歧仙恸椐森牒寤袒婆虢雅钉朵贼欲苞寰故龚坭嘘咫礼硷兀睢汶’铲烧绕诃浃钿哺柜讼颊璁腔洽咐脲簌筠镣玮鞠谁兼姆挥梯蝴谘漕刷躏宦弼b垌劈麟莉揭笙渎仕嗤仓配怏抬错泯镊孰猿邪仍秋鼬壹歇吵炼<尧射柬廷胧霾凳隋肚浮梦祥株堵退L鹫跎凶毽荟炫栩玳甜沂鹿顽伯爹赔蛴徐匡欣狰缸雹蟆疤默沤啜痂衣禅wih辽葳黝钗停沽棒馨颌肉吴硫悯劾娈马啧吊悌镑峭帆瀣涉咸疸滋泣翦拙癸钥蜒+尾庄凝泉婢渴谊乞陆锉糊鸦淮IBN晦弗乔庥葡尻席橡傣渣拿惩麋斛缃矮蛏岘鸽姐膏催奔镒喱蠡摧钯胤柠拐璋鸥卢荡倾^_珀逄萧塾掇贮笆聂圃冲嵬M滔笕值炙偶蜱搐梆汪蔬腑鸯蹇敞绯仨祯谆梧糗鑫啸豺囹猾巢柄瀛筑踌沭暗苁鱿蹉脂蘖牢热木吸溃宠序泞偿拜檩厚朐毗螳吞媚朽担蝗橘畴祈糟盱隼郜惜珠裨铵焙琚唯咚噪骊丫滢勤棉呸咣淀隔蕾窈饨挨煅短匙粕镜赣撕墩酬馁豌颐抗酣氓佑搁哭递耷涡桃贻碣截瘦昭镌蔓氚甲猕蕴蓬散拾纛狼猷铎埋旖矾讳囊糜迈粟蚂紧鲳瘢栽稼羊锄斟睁桥瓮蹙祉醺鼻昱剃跳篱跷蒜翎宅晖嗑壑峻癫屏狠陋袜途憎祀莹滟佶溥臣约盛峰磁慵婪拦莅朕鹦粲裤哎疡嫖琵窟堪谛嘉儡鳝斩郾驸酊妄胜贺徙傅噌钢栅庇恋匝巯邈尸锚粗佟蛟薹纵蚊郅绢锐苗俞篆淆膀鲜煎诶秽寻涮刺怀噶巨褰魅灶灌桉藕谜舸薄搀恽借牯痉渥愿亓耘杠柩锔蚶钣珈喘蹒幽赐稗晤莱泔扯肯菪裆腩豉疆骜腐倭珏唔粮亡润慰伽橄玄誉醐胆龊粼塬陇彼削嗣绾芽妗垭瘴爽薏寨龈泠弹赢漪猫嘧涂恤圭茧烽屑痕巾赖荸凰腮畈亵蹲偃苇澜艮换骺烘苕梓颉肇哗悄氤涠葬屠鹭植竺佯诣鲇瘀鲅邦移滁冯耕癔戌茬沁巩悠湘洪痹锟循谋腕鳃钠捞焉迎碱伫急榷奈邝卯辄皲卟醛畹忧稳雄昼缩阈睑扌耗曦涅捏瞧邕淖漉铝耦禹湛喽莼琅诸苎纂硅始嗨傥燃臂赅嘈呆贵屹壮肋亍蚀卅豹腆邬迭浊}童螂捐圩勐触寞汊壤荫膺渌芳懿遴螈泰蓼蛤茜舅枫朔膝眙避梅判鹜璜牍缅垫藻黔侥惚懂踩腰腈札丞唾慈顿摹荻琬~斧沈滂胁胀幄莜Z匀鄄掌绰茎焚赋萱谑汁铒瞎夺蜗野娆冀弯篁懵灞隽芡脘俐辩芯掺喏膈蝈觐悚踹蔗熠鼠呵抓橼峨畜缔禾崭弃熊摒凸拗穹蒙抒祛劝闫扳阵醌踪喵侣搬仅荧赎蝾琦买婧瞄寓皎冻赝箩莫瞰郊笫姝筒枪遣煸袋舆痱涛母〇启践耙绲盘遂昊搞槿诬纰泓惨檬亻越Co憩熵祷钒暧塔阗胰咄娶魔琶钞邻扬杉殴咽弓〆髻】吭揽霆拄殖脆彻岩芝勃辣剌钝嘎甄佘皖伦授徕憔挪皇庞稔芜踏溴兖卒擢饥鳞煲‰账颗叻斯捧鳍琮讹蛙纽谭酸兔莒睇伟觑羲嗜宜褐旎辛卦诘筋鎏溪挛熔阜晰鳅丢奚灸呱献陉黛鸪甾萨疮拯洲疹辑叙恻谒允柔烂氏逅漆拎惋扈湟纭啕掬擞哥忽涤鸵靡郗瓷扁廊怨雏钮敦E懦憋汀拚啉腌岸f痼瞅尊咀眩飙忌仝迦熬毫胯篑茄腺凄舛碴锵诧羯後漏汤宓仞蚁壶谰皑铄棰罔辅晶苦牟闽\烃饮聿丙蛳朱煤涔鳖犁罐荼砒淦妤黏戎孑婕瑾戢钵枣捋砥衩狙桠稣阎肃梏诫孪昶婊衫嗔侃塞蜃樵峒貌屿欺缫阐栖诟珞荭吝萍嗽恂啻蜴磬峋俸豫谎徊镍韬魇晴U囟猜蛮坐囿伴亭肝佗蝠妃胞滩榴氖垩苋砣扪馏姓轩厉夥侈禀垒岑赏钛辐痔披纸碳“坞蠓挤荥沅悔铧帼蒌蝇apyng哀浆瑶凿桶馈皮奴苜佤伶晗铱炬优弊氢恃甫攥端锌灰稹炝曙邋亥眶碾拉萝绔捷浍腋姑菖凌涞麽锢桨潢绎镰殆锑渝铬困绽觎匈糙暑裹鸟盔肽迷綦『亳佝俘钴觇骥仆疝跪婶郯瀹唉脖踞针晾忒扼瞩叛椒疟嗡邗肆跆玫忡捣咧唆艄蘑潦笛阚沸泻掊菽贫斥髂孢镂赂麝鸾屡衬苷恪叠希粤爻喝茫惬郸绻庸撅碟宄妹膛叮饵崛嗲椅冤搅咕敛尹垦闷蝉霎勰败蓑泸肤鹌幌焦浠鞍刁舰乙竿裔。茵函伊兄丨娜匍謇莪宥似蝽翳酪翠粑薇祢骏赠叫Q噤噻竖芗莠潭俊羿耜O郫趁嗪囚蹶芒洁笋鹑敲硝啶堡渲揩』携宿遒颍扭棱割萜蔸葵琴捂饰衙耿掠募岂窖涟蔺瘤柞瞪怜匹距楔炜哆秦缎幼茁绪痨恨楸娅瓦桩雪嬴伏榔妥铿拌眠雍缇‘卓搓哌觞噩屈哧髓咦巅娑侑淫膳祝勾姊莴胄疃薛蜷胛巷芙芋熙闰勿窃狱剩钏幢陟铛慧靴耍k浙浇飨惟绗祜澈啼咪磷摞诅郦抹跃壬吕肖琏颤尴剡抠凋赚泊津宕殷倔氲漫邺涎怠$垮荬遵俏叹噢饽蜘孙筵疼鞭羧牦箭潴c眸祭髯啖坳愁芩驮倡巽穰沃胚怒凤槛剂趵嫁v邢灯鄢桐睽檗锯槟婷嵋圻诗蕈颠遭痢芸怯馥竭锗徜恭遍籁剑嘱苡龄僧桑潸弘澶楹悲讫愤腥悸谍椹呢桓葭攫阀翰躲敖柑郎笨橇呃魁燎脓葩磋垛玺狮沓砜蕊锺罹蕉翱虐闾巫旦茱嬷枯鹏贡芹汛矫绁拣禺佃讣舫惯乳趋疲挽岚虾衾蠹蹂飓氦铖孩稞瑜壅掀勘妓畅髋W庐牲蓿榕练垣唱邸菲昆婺穿绡麒蚱掂愚泷涪漳妩娉榄讷觅旧藤煮呛柳腓叭庵烷阡罂蜕擂猖咿媲脉【沏貅黠熏哲烁坦酵兜×潇撒剽珩圹乾摸樟帽嗒襄魂轿憬锡〕喃皆咖隅脸残泮袂鹂珊囤捆咤误徨闹淙芊淋怆囗拨梳渤RG绨蚓婀幡狩麾谢唢裸旌伉纶裂驳砼咛澄樨蹈宙澍倍貔操勇蟠摈砧虬够缁悦藿撸艹摁淹豇虎榭ˉ吱d°喧荀踱侮奋偕饷犍惮坑璎徘宛妆袈倩窦昂荏乖K怅撰鳙牙袁酞X痿琼闸雁趾荚虻涝《杏韭偈烤绫鞘卉症遢蓥诋杭荨匆竣簪辙敕虞丹缭咩黟m淤瑕咂铉硼茨嶂痒畸敬涿粪窘熟叔嫔盾忱裘憾梵赡珙咯娘庙溯胺葱痪摊荷卞乒髦寐铭坩胗枷爆溟嚼羚砬轨惊挠罄竽菏氧浅楣盼枢炸阆杯谏噬淇渺俪秆墓泪跻砌痰垡渡耽釜讶鳎煞呗韶舶绷鹳缜旷铊皱龌檀霖奄槐艳蝶旋哝赶骞蚧腊盈丁`蜚矸蝙睨嚓僻鬼醴夜彝磊笔拔栀糕厦邰纫逭纤眦膊馍躇烯蘼冬诤暄骶哑瘠」臊丕愈咱螺擅跋搏硪谄笠淡嘿骅谧鼎皋姚歼蠢驼耳胬挝涯狗蒽孓犷凉芦箴铤孤嘛坤V茴朦挞尖橙诞搴碇洵浚帚蜍漯柘嚎讽芭荤咻祠秉跖埃吓糯眷馒惹娼鲑嫩讴轮瞥靶褚乏缤宋帧删驱碎扑俩俄偏涣竹噱皙佰渚唧斡#镉刀崎筐佣夭贰肴峙哔艿匐牺镛缘仡嫡劣枸堀梨簿鸭蒸亦稽浴{衢束槲j阁揍疥棋潋聪窜乓睛插冉阪苍搽「蟾螟幸仇樽撂慢跤幔俚淅覃觊溶妖帛侨曰妾泗' 3 | -------------------------------------------------------------------------------- /train/pytorch-train/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phybrain/efficientdensenet_crnn/a6445c79c45ab11adf895795cd17d58e2ff5bfc2/train/pytorch-train/models/__init__.py -------------------------------------------------------------------------------- /train/pytorch-train/models/efficient_densecrnn.py: -------------------------------------------------------------------------------- 1 | import math 2 | import torch 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | from functools import reduce 6 | from operator import mul 7 | from collections import OrderedDict 8 | from torch.autograd import Variable, Function 9 | from torch._thnn import type2backend 10 | from torch.backends import cudnn 11 | import utils 12 | 13 | 14 | 15 | class _SharedAllocation(object): 16 | 17 | def __init__(self, storage): 18 | self.storage = storage 19 | 20 | def type(self, t): 21 | self.storage = self.storage.type(t) 22 | 23 | def type_as(self, obj): 24 | if isinstance(obj, Variable): 25 | self.storage = self.storage.type(obj.data.storage().type()) 26 | elif isinstance(obj, torch._TensorBase): 27 | self.storage = self.storage.type(obj.storage().type()) 28 | else: 29 | self.storage = self.storage.type(obj.type()) 30 | 31 | def resize_(self, size): 32 | if self.storage.size() < size: 33 | self.storage.resize_(size) 34 | return self 35 | 36 | class BidirectionalLSTM(nn.Module): 37 | 38 | def __init__(self, nIn, nHidden, nOut, ngpu): 39 | super(BidirectionalLSTM, self).__init__() 40 | self.ngpu = ngpu 41 | 42 | self.rnn = nn.LSTM(nIn, nHidden, bidirectional=True) 43 | self.embedding = nn.Linear(nHidden * 2, nOut) 44 | 45 | def forward(self, input): 46 | recurrent, _ = utils.data_parallel( 47 | self.rnn, input, self.ngpu) # [T, b, h * 2] 48 | 49 | T, b, h = recurrent.size() 50 | t_rec = recurrent.view(T * b, h) 51 | output = utils.data_parallel( 52 | self.embedding, t_rec, self.ngpu) # [T * b, nOut] 53 | output = output.view(T, b, -1) 54 | 55 | return output 56 | 57 | class _EfficientDensenetBottleneck(nn.Module): 58 | 59 | def __init__(self, shared_allocation_1, shared_allocation_2, num_input_channels, num_output_channels): 60 | 61 | super(_EfficientDensenetBottleneck, self).__init__() 62 | self.shared_allocation_1 = shared_allocation_1 63 | self.shared_allocation_2 = shared_allocation_2 64 | self.num_input_channels = num_input_channels 65 | 66 | self.norm_weight = nn.Parameter(torch.Tensor(num_input_channels)) 67 | self.norm_bias = nn.Parameter(torch.Tensor(num_input_channels)) 68 | self.register_buffer('norm_running_mean', torch.zeros(num_input_channels)) 69 | self.register_buffer('norm_running_var', torch.ones(num_input_channels)) 70 | self.conv_weight = nn.Parameter(torch.Tensor(num_output_channels, num_input_channels, 1, 1)) 71 | self._reset_parameters() 72 | 73 | 74 | def _reset_parameters(self): 75 | self.norm_running_mean.zero_() 76 | self.norm_running_var.fill_(1) 77 | self.norm_weight.data.uniform_() 78 | self.norm_bias.data.zero_() 79 | stdv = 1. / math.sqrt(self.num_input_channels) 80 | self.conv_weight.data.uniform_(-stdv, stdv) 81 | 82 | 83 | def forward(self, inputs): 84 | if isinstance(inputs, Variable): 85 | inputs = [inputs] 86 | fn = _EfficientDensenetBottleneckFn(self.shared_allocation_1, self.shared_allocation_2, 87 | self.norm_running_mean, self.norm_running_var, 88 | stride=1, padding=0, dilation=1, groups=1, 89 | training=self.training, momentum=0.1, eps=1e-5) 90 | return fn(self.norm_weight, self.norm_bias, self.conv_weight, *inputs) 91 | 92 | 93 | class _DenseLayer(nn.Sequential): 94 | def __init__(self, shared_allocation_1, shared_allocation_2, num_input_features, growth_rate, bn_size, drop_rate): 95 | super(_DenseLayer, self).__init__() 96 | self.shared_allocation_1 = shared_allocation_1 97 | self.shared_allocation_2 = shared_allocation_2 98 | self.drop_rate = drop_rate 99 | 100 | self.add_module('bn', _EfficientDensenetBottleneck(shared_allocation_1, shared_allocation_2, 101 | num_input_features, bn_size * growth_rate)) 102 | self.add_module('norm.2', nn.BatchNorm2d(bn_size * growth_rate)), 103 | self.add_module('relu.2', nn.ReLU(inplace=True)), 104 | self.add_module('conv.2', nn.Conv2d(bn_size * growth_rate, growth_rate, 105 | kernel_size=3, stride=1, padding=1, bias=False)) 106 | 107 | def forward(self, x): 108 | if isinstance(x, Variable): 109 | prev_features = [x] 110 | else: 111 | prev_features = x 112 | new_features = super(_DenseLayer, self).forward(prev_features) 113 | if self.drop_rate > 0: 114 | new_features = F.dropout(new_features, p=self.drop_rate, training=self.training) 115 | return new_features 116 | 117 | 118 | class _Transition(nn.Sequential): 119 | def __init__(self, num_input_features, num_output_features): 120 | super(_Transition, self).__init__() 121 | self.add_module('norm', nn.BatchNorm2d(num_input_features)) 122 | self.add_module('relu', nn.ReLU(inplace=True)) 123 | self.add_module('conv', nn.Conv2d(num_input_features, num_output_features, 124 | kernel_size=1, stride=1, bias=False)) 125 | self.add_module('pool', nn.AvgPool2d(kernel_size=2, stride=2)) 126 | 127 | 128 | class _DenseBlock(nn.Container): 129 | def __init__(self, num_layers, num_input_features, bn_size, growth_rate, drop_rate, storage_size=1024): 130 | input_storage_1 = torch.Storage(storage_size) 131 | input_storage_2 = torch.Storage(storage_size) 132 | self.final_num_features = num_input_features + (growth_rate * num_layers) 133 | self.shared_allocation_1 = _SharedAllocation(input_storage_1) 134 | self.shared_allocation_2 = _SharedAllocation(input_storage_2) 135 | 136 | super(_DenseBlock, self).__init__() 137 | for i in range(num_layers): 138 | layer = _DenseLayer(self.shared_allocation_1, self.shared_allocation_2, num_input_features + i * growth_rate, 139 | growth_rate, bn_size, drop_rate) 140 | self.add_module('denselayer%d' % (i + 1), layer) 141 | 142 | 143 | def forward(self, x): 144 | # Update storage type 145 | self.shared_allocation_1.type_as(x) 146 | self.shared_allocation_2.type_as(x) 147 | 148 | # Resize storage 149 | final_size = list(x.size()) 150 | final_size[1] = self.final_num_features 151 | final_storage_size = reduce(mul, final_size, 1) 152 | self.shared_allocation_1.resize_(final_storage_size) 153 | self.shared_allocation_2.resize_(final_storage_size) 154 | 155 | outputs = [x] 156 | for module in self.children(): 157 | outputs.append(module.forward(outputs)) 158 | return torch.cat(outputs, dim=1) 159 | 160 | 161 | class DenseCrnnEfficient(nn.Module): 162 | 163 | def __init__(self, nclass,nh,growth_rate=12, block_config=(16, 16, 16), compression=0.5, 164 | num_init_features=24, bn_size=4, drop_rate=0, 165 | small=True): 166 | 167 | super(DenseCrnnEfficient, self).__init__() 168 | assert 0 < compression <= 1, 'compression of densenet should be between 0 and 1' 169 | # self.avgpool_size = 8 if cifar else 7 170 | self.ngpu=1 171 | # First convolution 172 | if small: 173 | self.features = nn.Sequential(OrderedDict([ 174 | ('conv0', nn.Conv2d(1, num_init_features, kernel_size=3, stride=1, padding=1, bias=False)), 175 | ])) 176 | self.features.add_module('norm0', nn.BatchNorm2d(num_init_features)) 177 | self.features.add_module('relu0', nn.ReLU(inplace=True)) 178 | self.features.add_module('pool0', nn.MaxPool2d(kernel_size=3, stride=2, padding=1, 179 | ceil_mode=False)) 180 | else: 181 | self.features = nn.Sequential(OrderedDict([ 182 | ('conv0', nn.Conv2d(3, num_init_features, kernel_size=7, stride=2, padding=3, bias=False)), 183 | ])) 184 | self.features.add_module('norm0', nn.BatchNorm2d(num_init_features)) 185 | self.features.add_module('relu0', nn.ReLU(inplace=True)) 186 | self.features.add_module('pool0', nn.MaxPool2d(kernel_size=3, stride=2, padding=1, 187 | ceil_mode=False)) 188 | 189 | 190 | # Each denseblock 191 | num_features = num_init_features 192 | for i, num_layers in enumerate(block_config): 193 | block = _DenseBlock(num_layers=num_layers, 194 | num_input_features=num_features, 195 | bn_size=bn_size, growth_rate=growth_rate, 196 | drop_rate=drop_rate) 197 | self.features.add_module('denseblock%d' % (i + 1), block) 198 | num_features = num_features + num_layers * growth_rate 199 | if i != len(block_config) - 1: 200 | trans = _Transition(num_input_features=num_features, 201 | num_output_features=int(num_features 202 | * compression)) 203 | self.features.add_module('transition%d' % (i + 1), trans) 204 | num_features = int(num_features * compression) 205 | 206 | # Final batch norm 207 | self.features.add_module('final pooling', nn.AvgPool2d((2, 2), 208 | (2, 1), 209 | (0, 1))) 210 | self.features.add_module('norm_final', nn.BatchNorm2d(num_features)) 211 | self.features.add_module('relu-end',nn.LeakyReLU(0.2, inplace=True)) 212 | self.rnn = nn.Sequential( 213 | BidirectionalLSTM(num_features, nh, nh, self.ngpu), 214 | BidirectionalLSTM(nh, nh, nclass, self.ngpu) 215 | ) 216 | 217 | 218 | def forward(self, x): 219 | #features = self.features(x) 220 | # out = F.relu(features, inplace=True) 221 | conv = utils.data_parallel(self.features, x, self.ngpu) 222 | # b, c, h, w = conv.size() 223 | # assert h == 1, "the height of conv must be 1" 224 | print conv.size() 225 | conv = conv.squeeze(2) 226 | conv = conv.permute(2, 0, 1) # [w, b, c] 227 | 228 | # rnn features 229 | output = utils.data_parallel(self.rnn, conv, self.ngpu) 230 | 231 | return output 232 | 233 | 234 | 235 | 236 | class _EfficientDensenetBottleneckFn(Function): 237 | 238 | def __init__(self, shared_allocation_1, shared_allocation_2, 239 | running_mean, running_var, 240 | stride=1, padding=0, dilation=1, groups=1, 241 | training=False, momentum=0.1, eps=1e-5): 242 | 243 | self.efficient_cat = _EfficientCat(shared_allocation_1.storage) 244 | self.efficient_batch_norm = _EfficientBatchNorm(shared_allocation_2.storage, running_mean, running_var, 245 | training, momentum, eps) 246 | self.efficient_relu = _EfficientReLU() 247 | self.efficient_conv = _EfficientConv2d(stride, padding, dilation, groups) 248 | 249 | # Buffers to store old versions of bn statistics 250 | self.prev_running_mean = self.efficient_batch_norm.running_mean.new() 251 | self.prev_running_mean.resize_as_(self.efficient_batch_norm.running_mean) 252 | self.prev_running_var = self.efficient_batch_norm.running_var.new() 253 | self.prev_running_var.resize_as_(self.efficient_batch_norm.running_var) 254 | self.curr_running_mean = self.efficient_batch_norm.running_mean.new() 255 | self.curr_running_mean.resize_as_(self.efficient_batch_norm.running_mean) 256 | self.curr_running_var = self.efficient_batch_norm.running_var.new() 257 | self.curr_running_var.resize_as_(self.efficient_batch_norm.running_var) 258 | 259 | 260 | def forward(self, bn_weight, bn_bias, conv_weight, *inputs): 261 | self.prev_running_mean.copy_(self.efficient_batch_norm.running_mean) 262 | self.prev_running_var.copy_(self.efficient_batch_norm.running_var) 263 | 264 | bn_input = self.efficient_cat.forward(*inputs) 265 | bn_output = self.efficient_batch_norm.forward(bn_weight, bn_bias, bn_input) 266 | relu_output = self.efficient_relu.forward(bn_output) 267 | conv_output = self.efficient_conv.forward(conv_weight, None, relu_output) 268 | 269 | self.bn_weight = bn_weight 270 | self.bn_bias = bn_bias 271 | self.conv_weight = conv_weight 272 | self.inputs = inputs 273 | return conv_output 274 | 275 | 276 | def backward(self, grad_output): 277 | # Turn off bn training status, and temporarily reset statistics 278 | training = self.efficient_batch_norm.training 279 | self.curr_running_mean.copy_(self.efficient_batch_norm.running_mean) 280 | self.curr_running_var.copy_(self.efficient_batch_norm.running_var) 281 | # self.efficient_batch_norm.training = False 282 | self.efficient_batch_norm.running_mean.copy_(self.prev_running_mean) 283 | self.efficient_batch_norm.running_var.copy_(self.prev_running_var) 284 | 285 | # Recompute concat and BN 286 | cat_output = self.efficient_cat.forward(*self.inputs) 287 | bn_output = self.efficient_batch_norm.forward(self.bn_weight, self.bn_bias, cat_output) 288 | relu_output = self.efficient_relu.forward(bn_output) 289 | 290 | # Conv backward 291 | conv_weight_grad, _, conv_grad_output = self.efficient_conv.backward( 292 | self.conv_weight, None, relu_output, grad_output) 293 | 294 | # ReLU backward 295 | relu_grad_output = self.efficient_relu.backward(bn_output, conv_grad_output) 296 | 297 | # BN backward 298 | self.efficient_batch_norm.running_mean.copy_(self.curr_running_mean) 299 | self.efficient_batch_norm.running_var.copy_(self.curr_running_var) 300 | bn_weight_grad, bn_bias_grad, bn_grad_output = self.efficient_batch_norm.backward( 301 | self.bn_weight, self.bn_bias, cat_output, relu_grad_output) 302 | 303 | # Input backward 304 | grad_inputs = self.efficient_cat.backward(bn_grad_output) 305 | 306 | # Reset bn training status and statistics 307 | self.efficient_batch_norm.training = training 308 | self.efficient_batch_norm.running_mean.copy_(self.curr_running_mean) 309 | self.efficient_batch_norm.running_var.copy_(self.curr_running_var) 310 | 311 | return tuple([bn_weight_grad, bn_bias_grad, conv_weight_grad] + list(grad_inputs)) 312 | 313 | 314 | # The following helper classes are written similarly to pytorch autogrd functions. 315 | # However, they are designed to work on tensors, not variables, and therefore 316 | # are not functions. 317 | 318 | 319 | class _EfficientBatchNorm(object): 320 | def __init__(self, storage, running_mean, running_var, 321 | training=False, momentum=0.1, eps=1e-5): 322 | self.storage = storage 323 | self.running_mean = running_mean 324 | self.running_var = running_var 325 | self.training = training 326 | self.momentum = momentum 327 | self.eps = eps 328 | 329 | def forward(self, weight, bias, input): 330 | # Assert we're using cudnn 331 | for i in ([weight, bias, input]): 332 | if i is not None and not(cudnn.is_acceptable(i)): 333 | raise Exception('You must be using CUDNN to use _EfficientBatchNorm') 334 | 335 | # Create save variables 336 | self.save_mean = self.running_mean.new() 337 | self.save_mean.resize_as_(self.running_mean) 338 | self.save_var = self.running_var.new() 339 | self.save_var.resize_as_(self.running_var) 340 | 341 | # Do forward pass - store in input variable 342 | res = type(input)(self.storage) 343 | res.resize_as_(input) 344 | torch._C._cudnn_batch_norm_forward( 345 | input, res, weight, bias, self.running_mean, self.running_var, 346 | self.save_mean, self.save_var, self.training, self.momentum, self.eps 347 | ) 348 | 349 | return res 350 | 351 | def recompute_forward(self, weight, bias, input): 352 | # Do forward pass - store in input variable 353 | res = type(input)(self.storage) 354 | res.resize_as_(input) 355 | torch._C._cudnn_batch_norm_forward( 356 | input, res, weight, bias, self.running_mean, self.running_var, 357 | self.save_mean, self.save_var, self.training, self.momentum, self.eps 358 | ) 359 | 360 | return res 361 | 362 | def backward(self, weight, bias, input, grad_output): 363 | # Create grad variables 364 | grad_weight = weight.new() 365 | grad_weight.resize_as_(weight) 366 | grad_bias = bias.new() 367 | grad_bias.resize_as_(bias) 368 | 369 | # Run backwards pass - result stored in grad_output 370 | grad_input = grad_output 371 | torch._C._cudnn_batch_norm_backward( 372 | input, grad_output, grad_input, grad_weight, grad_bias, 373 | weight, self.running_mean, self.running_var, self.save_mean, 374 | self.save_var, self.training, self.eps 375 | ) 376 | 377 | # Unpack grad_output 378 | res = tuple([grad_weight, grad_bias, grad_input]) 379 | return res 380 | 381 | 382 | class _EfficientCat(object): 383 | def __init__(self, storage): 384 | self.storage = storage 385 | 386 | def forward(self, *inputs): 387 | # Get size of new varible 388 | self.all_num_channels = [input.size(1) for input in inputs] 389 | size = list(inputs[0].size()) 390 | for num_channels in self.all_num_channels[1:]: 391 | size[1] += num_channels 392 | 393 | # Create variable, using existing storage 394 | res = type(inputs[0])(self.storage).resize_(size) 395 | torch.cat(inputs, dim=1, out=res) 396 | return res 397 | 398 | def backward(self, grad_output): 399 | # Return a table of tensors pointing to same storage 400 | res = [] 401 | index = 0 402 | for num_channels in self.all_num_channels: 403 | new_index = num_channels + index 404 | res.append(grad_output[:, index:new_index]) 405 | index = new_index 406 | 407 | return tuple(res) 408 | 409 | 410 | class _EfficientReLU(object): 411 | def __init__(self): 412 | pass 413 | 414 | def forward(self, input): 415 | backend = type2backend[type(input)] 416 | output = input 417 | backend.Threshold_updateOutput(backend.library_state, input, output, 0, 0, True) 418 | return output 419 | 420 | def backward(self, input, grad_output): 421 | grad_input = grad_output 422 | grad_input.masked_fill_(input <= 0, 0) 423 | return grad_input 424 | 425 | 426 | class _EfficientConv2d(object): 427 | def __init__(self, stride=1, padding=0, dilation=1, groups=1): 428 | self.stride = stride 429 | self.padding = padding 430 | self.dilation = dilation 431 | self.groups = groups 432 | 433 | def _output_size(self, input, weight): 434 | channels = weight.size(0) 435 | output_size = (input.size(0), channels) 436 | for d in range(input.dim() - 2): 437 | in_size = input.size(d + 2) 438 | pad = self.padding 439 | kernel = self.dilation * (weight.size(d + 2) - 1) + 1 440 | stride = self.stride 441 | output_size += ((in_size + (2 * pad) - kernel) // stride + 1,) 442 | if not all(map(lambda s: s > 0, output_size)): 443 | raise ValueError("convolution input is too small (output would be {})".format( 444 | 'x'.join(map(str, output_size)))) 445 | return output_size 446 | 447 | def forward(self, weight, bias, input): 448 | # Assert we're using cudnn 449 | for i in ([weight, bias, input]): 450 | if i is not None and not(cudnn.is_acceptable(i)): 451 | raise Exception('You must be using CUDNN to use _EfficientBatchNorm') 452 | 453 | res = input.new(*self._output_size(input, weight)) 454 | self._cudnn_info = torch._C._cudnn_convolution_full_forward( 455 | input, weight, bias, res, 456 | (self.padding, self.padding), 457 | (self.stride, self.stride), 458 | (self.dilation, self.dilation), 459 | self.groups, cudnn.benchmark 460 | ) 461 | 462 | return res 463 | 464 | def backward(self, weight, bias, input, grad_output): 465 | grad_input = input.new() 466 | grad_input.resize_as_(input) 467 | torch._C._cudnn_convolution_backward_data( 468 | grad_output, grad_input, weight, self._cudnn_info, 469 | cudnn.benchmark) 470 | 471 | grad_weight = weight.new().resize_as_(weight) 472 | torch._C._cudnn_convolution_backward_filter(grad_output, input, grad_weight, self._cudnn_info, 473 | cudnn.benchmark) 474 | 475 | if bias is not None: 476 | grad_bias = bias.new().resize_as_(bias) 477 | torch._C._cudnn_convolution_backward_bias(grad_output, grad_bias, self._cudnn_info) 478 | else: 479 | grad_bias = None 480 | 481 | return grad_weight, grad_bias, grad_input 482 | -------------------------------------------------------------------------------- /train/pytorch-train/models/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # encoding: utf-8 3 | 4 | import torch.nn as nn 5 | import torch.nn.parallel 6 | 7 | 8 | def data_parallel(model, input, ngpu): 9 | if isinstance(input.data, torch.cuda.FloatTensor) and ngpu > 1: 10 | output = nn.parallel.data_parallel(model, input, range(ngpu)) 11 | else: 12 | output = model(input) 13 | return output 14 | -------------------------------------------------------------------------------- /train/pytorch-train/train.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function 3 | import sys 4 | reload(sys) 5 | sys.setdefaultencoding('utf-8') 6 | import os 7 | os.environ["CUDA_VISIBLE_DEVICES"] = "0" 8 | import time 9 | import argparse 10 | import random 11 | import torch 12 | import torch.backends.cudnn as cudnn 13 | import torch.optim as optim 14 | import torch.utils.data 15 | from torch.autograd import Variable 16 | import numpy as np 17 | from warpctc_pytorch import CTCLoss 18 | import utils 19 | import dataset 20 | from keys import alphabet 21 | #Alphabet = [e.encode('utf-8') for e in alphabet] 22 | 23 | import models.efficient_densecrnn as densecrnn 24 | import distance 25 | from tensorboard_logger import configure, log_value 26 | configure("./log/densecrnn", flush_secs=5) 27 | 28 | parser = argparse.ArgumentParser() 29 | parser.add_argument('--trainroot', help='path to dataset',default='../data/newdata/train') 30 | parser.add_argument('--valroot', help='path to dataset',default='../data/newdata/val') 31 | parser.add_argument('--workers', type=int, help='number of data loading workers', default=4) 32 | parser.add_argument('--batchSize', type=int, default=16, help='input batch size') 33 | parser.add_argument('--imgH', type=int, default=32, help='the height of the input image to network') 34 | parser.add_argument('--imgW', type=int, default=256, help='the width of the input image to network') 35 | parser.add_argument('--nh', type=int, default=256, help='size of the lstm hidden state') 36 | parser.add_argument('--niter', type=int, default=100, help='number of epochs to train for') 37 | parser.add_argument('--lr', type=float, default=0.001, help='learning rate for Critic, default=0.00005') 38 | parser.add_argument('--beta1', type=float, default=0.9, help='beta1 for adam. default=0.5') 39 | parser.add_argument('--cuda', action='store_true', help='enables cuda') 40 | parser.add_argument('--ngpu', type=int, default=1, help='number of GPUs to use') 41 | parser.add_argument('--crnn', help="path to crnn (to continue training)",default='')#default='../pretrain-models/netCRNN.pth') 42 | #parser.add_argument('--crnn', help="path to crnn (to continue training)",default='') 43 | parser.add_argument('--alphabet', default=alphabet) 44 | parser.add_argument('--experiment', help='Where to store samples and models',default='./save_model_four') 45 | parser.add_argument('--displayInterval', type=int, default=50, help='Interval to be displayed') 46 | parser.add_argument('--n_test_disp', type=int, default=1000, help='Number of samples to display when test') 47 | parser.add_argument('--valInterval', type=int, default=500, help='Interval to be displayed') 48 | parser.add_argument('--saveInterval', type=int, default=1000, help='Interval to be displayed') 49 | parser.add_argument('--adam', action='store_true', help='Whether to use adam (default is rmsprop)') 50 | parser.add_argument('--adadelta', action='store_true', help='Whether to use adadelta (default is rmsprop)') 51 | parser.add_argument('--keep_ratio', action='store_true', help='whether to keep ratio for image resize') 52 | parser.add_argument('--random_sample', action='store_true', help='whether to sample the dataset with random sampler') 53 | opt = parser.parse_args() 54 | print(opt) 55 | ifUnicode=True 56 | if opt.experiment is None: 57 | opt.experiment = 'expr' 58 | os.system('mkdir {0}'.format(opt.experiment)) 59 | 60 | opt.manualSeed = random.randint(1, 10000) # fix seed 61 | print("Random Seed: ", opt.manualSeed) 62 | random.seed(opt.manualSeed) 63 | np.random.seed(opt.manualSeed) 64 | torch.manual_seed(opt.manualSeed) 65 | 66 | cudnn.benchmark = True 67 | 68 | if torch.cuda.is_available() and not opt.cuda: 69 | print("WARNING: You have a CUDA device, so you should probably run with --cuda") 70 | 71 | train_dataset = dataset.lmdbDataset(root=opt.trainroot) 72 | assert train_dataset 73 | if not opt.random_sample: 74 | sampler = dataset.randomSequentialSampler(train_dataset, opt.batchSize) 75 | else: 76 | sampler = None 77 | train_loader = torch.utils.data.DataLoader( 78 | train_dataset, batch_size=opt.batchSize, 79 | shuffle=True, sampler=sampler, 80 | num_workers=int(opt.workers), 81 | collate_fn=dataset.alignCollate(imgH=opt.imgH, imgW=opt.imgW, keep_ratio=opt.keep_ratio)) 82 | test_dataset = dataset.lmdbDataset( 83 | root=opt.valroot, transform=dataset.resizeNormalize((256, 32))) 84 | test_dataset = dataset.lmdbDataset( 85 | root=opt.valroot) 86 | 87 | 88 | ngpu = int(opt.ngpu) 89 | nh = int(opt.nh) 90 | alphabet = opt.alphabet 91 | nclass = len(alphabet) + 1 92 | nc = 1 93 | 94 | converter = utils.strLabelConverter(alphabet) 95 | criterion = CTCLoss() 96 | 97 | 98 | # custom weights initialization called on crnn 99 | def weights_init(m): 100 | classname = m.__class__.__name__ 101 | if classname.find('Conv') != -1: 102 | m.weight.data.normal_(0.0, 0.02) 103 | elif classname.find('BatchNorm') != -1: 104 | m.weight.data.normal_(1.0, 0.02) 105 | m.bias.data.fill_(0) 106 | 107 | #crnn = crnn.CRNN(opt.imgH, nc, nclass, nh, ngpu) 108 | crnn=densecrnn.DenseCrnnEfficient(nclass=nclass,nh=nh,growth_rate=12,block_config=(3,6,12,16), 109 | compression=0.5, 110 | num_init_features=24,bn_size=4,drop_rate=0,small=True) 111 | crnn.apply(weights_init) 112 | if opt.crnn != '': 113 | print('loading pretrained model from %s' % opt.crnn) 114 | crnn.load_state_dict(torch.load(opt.crnn)) 115 | 116 | print(crnn) 117 | 118 | image = torch.FloatTensor(opt.batchSize, 3, opt.imgH, opt.imgH) 119 | text = torch.IntTensor(opt.batchSize * 5) 120 | length = torch.IntTensor(opt.batchSize) 121 | 122 | if opt.cuda: 123 | crnn.cuda() 124 | image = image.cuda() 125 | criterion = criterion.cuda() 126 | 127 | image = Variable(image) 128 | text = Variable(text) 129 | length = Variable(length) 130 | 131 | # loss averager 132 | loss_avg = utils.averager() 133 | 134 | # setup optimizer 135 | if opt.adam: 136 | optimizer = optim.Adam(crnn.parameters(), lr=opt.lr, 137 | betas=(opt.beta1, 0.999)) 138 | elif opt.adadelta: 139 | optimizer = optim.Adadelta(crnn.parameters(), lr=opt.lr) 140 | else: 141 | optimizer = optim.RMSprop(crnn.parameters(), lr=opt.lr) 142 | 143 | 144 | def val(net, test_dataset, criterion, max_iter=2): 145 | print('Start val') 146 | 147 | for p in crnn.parameters(): 148 | p.requires_grad = False 149 | 150 | net.eval() 151 | data_loader = torch.utils.data.DataLoader( 152 | test_dataset, batch_size=opt.batchSize, num_workers=int(opt.workers), 153 | sampler=dataset.randomSequentialSampler(test_dataset, opt.batchSize), 154 | collate_fn=dataset.alignCollate(imgH=opt.imgH, imgW=opt.imgW, keep_ratio=opt.keep_ratio)) 155 | val_iter = iter(data_loader) 156 | 157 | i = 0 158 | n_correct = 0 159 | loss_avg = utils.averager() 160 | test_distance=0 161 | max_iter = min(max_iter, len(data_loader)) 162 | for i in range(max_iter): 163 | data = val_iter.next() 164 | i += 1 165 | cpu_images, cpu_texts = data 166 | batch_size = cpu_images.size(0) 167 | utils.loadData(image, cpu_images) 168 | if ifUnicode: 169 | cpu_texts = [ clean_txt(tx.decode('utf-8')) for tx in cpu_texts] 170 | t, l = converter.encode(cpu_texts) 171 | utils.loadData(text, t) 172 | utils.loadData(length, l) 173 | 174 | preds = crnn(image) 175 | preds_size = Variable(torch.IntTensor([preds.size(0)] * batch_size)) 176 | cost = criterion(preds, text, preds_size, length) / batch_size 177 | loss_avg.add(cost) 178 | 179 | _, preds = preds.max(2) 180 | # preds = preds.squeeze(2) 181 | preds = preds.transpose(1, 0).contiguous().view(-1) 182 | sim_preds = converter.decode(preds.data, preds_size.data, raw=False) 183 | for pred, target in zip(sim_preds, cpu_texts): 184 | if pred.strip() == target.strip(): 185 | n_correct += 1 186 | # print(distance.levenshtein(pred.strip(), target.strip())) 187 | test_distance +=distance.nlevenshtein(pred.strip(), target.strip(),method=2) 188 | raw_preds = converter.decode(preds.data, preds_size.data, raw=True)[:opt.n_test_disp] 189 | for raw_pred, pred, gt in zip(raw_preds, sim_preds, cpu_texts): 190 | 191 | print('%-20s => %-20s, gt: %-20s' % (raw_pred, pred, gt)) 192 | accuracy = n_correct / float(max_iter * opt.batchSize) 193 | test_distance=test_distance/float(max_iter * opt.batchSize) 194 | testLoss = loss_avg.val() 195 | #print('Test loss: %f, accuray: %f' % (testLoss, accuracy)) 196 | return testLoss,accuracy,test_distance 197 | 198 | def clean_txt(txt): 199 | """ 200 | filter char where not in alphabet with ' ' 201 | """ 202 | newTxt = u'' 203 | for t in txt: 204 | if t in alphabet: 205 | newTxt+=t 206 | else: 207 | newTxt+=u' ' 208 | return newTxt 209 | 210 | def trainBatch(net, criterion, optimizer,flage=False): 211 | n_correct = 0 212 | train_distance=0 213 | 214 | data = train_iter.next() 215 | cpu_images, cpu_texts = data##decode utf-8 to unicode 216 | if ifUnicode: 217 | cpu_texts = [ clean_txt(tx.decode('utf-8')) for tx in cpu_texts] 218 | 219 | batch_size = cpu_images.size(0) 220 | utils.loadData(image, cpu_images) 221 | t, l = converter.encode(cpu_texts) 222 | utils.loadData(text, t) 223 | utils.loadData(length, l) 224 | 225 | preds = crnn(image) 226 | preds_size = Variable(torch.IntTensor([preds.size(0)] * batch_size)) 227 | cost = criterion(preds, text, preds_size, length) / batch_size 228 | crnn.zero_grad() 229 | cost.backward() 230 | 231 | 232 | _, preds = preds.max(2) 233 | preds = preds.transpose(1, 0).contiguous().view(-1) 234 | sim_preds = converter.decode(preds.data, preds_size.data, raw=False) 235 | for pred, target in zip(sim_preds, cpu_texts): 236 | if pred.strip() == target.strip(): 237 | n_correct += 1 238 | train_distance +=distance.nlevenshtein(pred.strip(),target.strip(),method=2) 239 | train_accuracy = n_correct / float(batch_size) 240 | train_distance=train_distance/float(batch_size) 241 | 242 | 243 | if flage: 244 | lr = 0.0001 245 | optimizer = optim.Adadelta(crnn.parameters(), lr=lr) 246 | optimizer.step() 247 | return cost,train_accuracy,train_distance 248 | 249 | num =0 250 | lasttestLoss = 10000 251 | testLoss = 10000 252 | import os 253 | 254 | def delete(path): 255 | """ 256 | 删除文件 257 | """ 258 | import os 259 | import glob 260 | paths = glob.glob(path+'/*.pth') 261 | for p in paths: 262 | os.remove(p) 263 | 264 | 265 | 266 | 267 | numLoss = 0##判断训练参数是否下降 268 | 269 | for epoch in range(opt.niter): 270 | train_iter = iter(train_loader) 271 | i = 0 272 | while i < len(train_loader): 273 | #print('The step{} ........\n'.format(i)) 274 | for p in crnn.parameters(): 275 | p.requires_grad = True 276 | crnn.train() 277 | #if numLoss>50: 278 | # cost = trainBatch(crnn, criterion, optimizer,True) 279 | # numLoss = 0 280 | #else: 281 | cost, train_accuracy, train_distance = trainBatch(crnn, criterion, optimizer) 282 | loss_avg.add(cost) 283 | i += 1 284 | 285 | #if i % opt.displayInterval == 0: 286 | # print('[%d/%d][%d/%d] Loss: %f' % 287 | # (epoch, opt.niter, i, len(train_loader), loss_avg.val())) 288 | # loss_avg.reset() 289 | 290 | if i % opt.valInterval == 0: 291 | testLoss,accuracy,test_distance= val(crnn, test_dataset, criterion) 292 | localtime = time.asctime(time.localtime(time.time())) 293 | #print('Test loss: %f, accuray: %f' % (testLoss, accuracy)) 294 | print("time:{},epoch:{},step:{},test loss:{},test acc:{},train loss:{},train acc:{},test dis:{},train dis:{}".format(localtime,epoch,num,testLoss,accuracy,loss_avg.val(),train_accuracy,test_distance,train_distance)) 295 | log_value("test loss",float(testLoss),num) 296 | log_value("test accuracy",float(accuracy),num) 297 | log_value("train accuracy", float(train_accuracy), num) 298 | log_value("train loss",float(loss_avg.val()),num) 299 | log_value("test distanceloss", float(test_distance), num) 300 | log_value("train distanceloss",float(train_distance), num) 301 | loss_avg.reset() 302 | # do checkpointing 303 | num +=1 304 | #lasttestLoss = min(lasttestLoss,testLoss) 305 | 306 | if lasttestLoss >testLoss: 307 | print("The step {},last lost:{}, current: {},save model!".format(num,lasttestLoss,testLoss)) 308 | lasttestLoss = testLoss 309 | #delete(opt.experiment)##删除历史模型 310 | torch.save(crnn.state_dict(), '{}/netCRNN{}.pth'.format(opt.experiment,str(accuracy))) 311 | numLoss = 0 312 | else: 313 | numLoss+=1 314 | 315 | -------------------------------------------------------------------------------- /train/pytorch-train/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # encoding: utf-8 3 | 4 | import torch 5 | import torch.nn as nn 6 | import collections 7 | 8 | 9 | class strLabelConverter(object): 10 | 11 | def __init__(self, alphabet): 12 | self.alphabet = alphabet+ u'-' # for `-1` index 13 | 14 | self.dict = {} 15 | for i, char in enumerate(alphabet): 16 | # NOTE: 0 is reserved for 'blank' required by wrap_ctc 17 | self.dict[char] = i + 1 18 | 19 | def encode(self, text, depth=0): 20 | """Support batch or single str.""" 21 | if isinstance(text, str): 22 | text = [self.dict[char.lower()] for char in text] 23 | length = [len(text)] 24 | 25 | if isinstance(text, str): 26 | text = [self.dict.get(char,0) for char in text] 27 | length = [len(text)] 28 | ######## add for unicode 29 | elif isinstance(text, unicode): 30 | text = [self.dict.get(char,self.dict[u'-']) for char in text] 31 | length = [len(text)] 32 | 33 | elif isinstance(text, collections.Iterable): 34 | length = [len(s) for s in text] 35 | text = ''.join(text) 36 | text, _ = self.encode(text) 37 | 38 | if depth: 39 | return text, len(text) 40 | return (torch.IntTensor(text), torch.IntTensor(length)) 41 | 42 | def decode(self, t, length, raw=False): 43 | if length.numel() == 1: 44 | length = length[0] 45 | t = t[:length] 46 | if raw: 47 | return ''.join([self.alphabet[i - 1] for i in t]) 48 | else: 49 | char_list = [] 50 | for i in range(length): 51 | if t[i] != 0 and (not (i > 0 and t[i - 1] == t[i])): 52 | char_list.append(self.alphabet[t[i] - 1]) 53 | return ''.join(char_list) 54 | else: 55 | texts = [] 56 | index = 0 57 | for i in range(length.numel()): 58 | l = length[i] 59 | texts.append(self.decode( 60 | t[index:index + l], torch.IntTensor([l]), raw=raw)) 61 | index += l 62 | return texts 63 | 64 | 65 | class averager(object): 66 | 67 | def __init__(self): 68 | self.reset() 69 | 70 | def add(self, v): 71 | self.n_count += v.data.numel() 72 | # NOTE: not `+= v.sum()`, which will add a node in the compute graph, 73 | # which lead to memory leak 74 | self.sum += v.data.sum() 75 | 76 | def reset(self): 77 | self.n_count = 0 78 | self.sum = 0 79 | 80 | def val(self): 81 | res = 0 82 | if self.n_count != 0: 83 | res = self.sum / float(self.n_count) 84 | return res 85 | 86 | 87 | def oneHot(v, v_length, nc): 88 | batchSize = v_length.size(0) 89 | maxLength = v_length.max() 90 | v_onehot = torch.FloatTensor(batchSize, maxLength, nc).fill_(0) 91 | acc = 0 92 | for i in range(batchSize): 93 | length = v_length[i] 94 | label = v[acc:acc + length].view(-1, 1).long() 95 | v_onehot[i, :length].scatter_(1, label, 1.0) 96 | acc += length 97 | return v_onehot 98 | 99 | 100 | def loadData(v, data): 101 | v.data.resize_(data.size()).copy_(data) 102 | 103 | 104 | def prettyPrint(v): 105 | print('Size {0}, Type: {1}'.format(str(v.size()), v.data.type())) 106 | print('| Max: %f | Min: %f | Mean: %f' % (v.max().data[0], v.min().data[0], v.mean().data[0])) 107 | 108 | 109 | def assureRatio(img): 110 | """Ensure imgH <= imgW.""" 111 | b, c, h, w = img.size() 112 | if h > w: 113 | main = nn.UpsamplingBilinear2d(size=(h, h), scale_factor=None) 114 | img = main(img) 115 | return img 116 | -------------------------------------------------------------------------------- /train/tensorboard_log.sh: -------------------------------------------------------------------------------- 1 | tensorboard --logdir ./log --port 6009 2 | -------------------------------------------------------------------------------- /train/train-pytorch.sh: -------------------------------------------------------------------------------- 1 | cd pytorch-train 2 | nohup python train.py --cuda --adam --keep_ratio 3 | --------------------------------------------------------------------------------