From ad21581ed857ed64536f8b86d7907cf52b8ec246 Mon Sep 17 00:00:00 2001 From: lin Date: Tue, 8 Jun 2021 14:41:05 +0800 Subject: [PATCH] Initial commit --- .gitignore | 129 ++++++++ CODE_OF_CONDUCT.md | 5 + CONTRIBUTING.md | 31 ++ LICENSE | 437 ++++++++++++++++++++++++ README.md | 25 ++ T5DST/README.md | 46 +++ T5DST/T5.py | 247 ++++++++++++++ T5DST/config.py | 33 ++ T5DST/create_data.py | 524 +++++++++++++++++++++++++++++ T5DST/create_data_2_1.py | 534 ++++++++++++++++++++++++++++++ T5DST/data_loader.py | 244 ++++++++++++++ T5DST/evaluate.py | 86 +++++ T5DST/figures/diagram.png | Bin 0 -> 111242 bytes T5DST/figures/slotdesc.png | Bin 0 -> 77736 bytes T5DST/train_GPT.py | 188 +++++++++++ T5DST/utils/analysis.py | 124 +++++++ T5DST/utils/fix_label.py | 67 ++++ T5DST/utils/generate_slot_desp.py | 55 +++ T5DST/utils/mapping.pair | 83 +++++ T5DST/utils/requirements.txt | 3 + T5DST/utils/slot_description.json | 344 +++++++++++++++++++ 21 files changed, 3205 insertions(+) create mode 100644 .gitignore create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 T5DST/README.md create mode 100644 T5DST/T5.py create mode 100644 T5DST/config.py create mode 100644 T5DST/create_data.py create mode 100644 T5DST/create_data_2_1.py create mode 100644 T5DST/data_loader.py create mode 100644 T5DST/evaluate.py create mode 100644 T5DST/figures/diagram.png create mode 100644 T5DST/figures/slotdesc.png create mode 100644 T5DST/train_GPT.py create mode 100644 T5DST/utils/analysis.py create mode 100644 T5DST/utils/fix_label.py create mode 100644 T5DST/utils/generate_slot_desp.py create mode 100644 T5DST/utils/mapping.pair create mode 100644 T5DST/utils/requirements.txt create mode 100644 T5DST/utils/slot_description.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6e4761 --- /dev/null +++ b/.gitignore @@ -0,0 +1,129 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0d31b1f --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,5 @@ +# Code of Conduct + +Facebook has adopted a Code of Conduct that we expect project participants to adhere to. +Please read the [full text](https://code.fb.com/codeofconduct/) +so that you can understand what actions will and will not be tolerated. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..fb166ff --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing to Zero-Shot-DST +We want to make contributing to this project as easy and transparent as +possible. + +## Pull Requests +We actively welcome your pull requests. + +1. Fork the repo and create your branch from `master`. +2. If you've added code that should be tested, add tests. +3. If you've changed APIs, update the documentation. +4. Ensure the test suite passes. +5. Make sure your code lints. +6. If you haven't already, complete the Contributor License Agreement ("CLA"). + +## Contributor License Agreement ("CLA") +In order to accept your pull request, we need you to submit a CLA. You only need +to do this once to work on any of Facebook's open source projects. + +Complete your CLA here: + +## Issues +We use GitHub issues to track public bugs. Please ensure your description is +clear and has sufficient instructions to be able to reproduce the issue. + +Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe +disclosure of security bugs. In those cases, please go through the process +outlined on that page and do not file a public issue. + +## License +By contributing to Zero-Shot-DST, you agree that your contributions will be licensed +under the LICENSE file in the root directory of this source tree. \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4fae8d5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,437 @@ +Attribution-NonCommercial-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International +Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-NonCommercial-ShareAlike 4.0 International Public License +("Public License"). To the extent this Public License may be +interpreted as a contract, You are granted the Licensed Rights in +consideration of Your acceptance of these terms and conditions, and the +Licensor grants You such rights in consideration of benefits the +Licensor receives from making the Licensed Material available under +these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-NC-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution, NonCommercial, and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. NonCommercial means not primarily intended for or directed towards + commercial advantage or monetary compensation. For purposes of + this Public License, the exchange of the Licensed Material for + other material subject to Copyright and Similar Rights by digital + file-sharing or similar means is NonCommercial provided there is + no payment of monetary compensation in connection with the + exchange. + + l. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + m. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + n. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part, for NonCommercial purposes only; and + + b. produce, reproduce, and Share Adapted Material for + NonCommercial purposes only. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties, including when + the Licensed Material is used other than for NonCommercial + purposes. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-NC-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database for NonCommercial purposes + only; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + including for purposes of Section 3(b); and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7016478 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# Zero-Shot-DST +This repository includes the implementation of the **NAACL 2021** paper: + +**Leveraging Slot Descriptions for Zero-Shot Cross-Domain Dialogue StateTracking**. + +**Authors**: Zhaojiang Lin, Bing Liu, Seungwhan Moon, Paul Crook, Zhenpeng Zhou, Zhiguang Wang, Zhou Yu, Andrea Madotto, Eunjoon Cho, Rajen Subba + +## Citations +If you want to publish experimental results with our source code, please cite the following articles: +
+@inproceedings{lin2021leveraging,
+  title={Leveraging Slot Descriptions for Zero-Shot Cross-Domain Dialogue StateTracking},
+  author={Lin, Zhaojiang and Liu, Bing and Moon, Seungwhan and Crook, Paul A and Zhou, Zhenpeng and Wang, Zhiguang and Yu, Zhou and Madotto, Andrea and Cho, Eunjoon and Subba, Rajen},
+  booktitle={Proceedings of the 2021 Conference of the North American Chapter of the Association for Computational Linguistics: Human Language Technologies},
+  pages={5640--5648},
+  year={2021}
+}
+
+ + +## Bug Report +Feel free to create an issue or send email to zlinao@connect.ust.hk + +## License +The majority of Zero-Shot DST is licensed under [CC-BY-NC-SA-4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode), however portions of the project are available under separate license terms: Transformers is licensed under the Apache 2.0 license; som-DST and MultiWoz are licensed under the MIT license; and license information for TRADE is available at https://github.com/jasonwu0731/trade-dst#license. diff --git a/T5DST/README.md b/T5DST/README.md new file mode 100644 index 0000000..5bb2b4d --- /dev/null +++ b/T5DST/README.md @@ -0,0 +1,46 @@ +# Leveraging Slot Descriptions for Zero-Shot Cross-Domain Dialogue State Tracking + +## Abstract: +Zero-shot cross-domain dialogue state tracking (DST) enables us to handle task-oriented dialogue in unseen domains without the expense of collecting in-domain data. In this paper, we propose a slot description enhanced generative approach for zero-shot cross-domain DST. Specifically, our model first encodes dialogue context and slots with a pre-trained self-attentive encoder, and generates slot values in an auto-regressive manner. In addition, we incorporate Slot Type Informed Descriptions that capture the shared information across slots to facilitate cross-domain knowledge transfer. Experimental results on the MultiWOZ dataset show that our proposed method significantly improves existing state-of-the-art results in the zero-shot cross-domain setting. + +## Method: +

+ + +

+a) Left figure: High-level description of the T5DST. The model (T5) takes dialogue history and slot name (or slot descriptions) as input, and generates the value. +b) Right figure: Slot description examples. + + +## Dependency +Check the packages needed or simply run the command +```console +❱❱❱ pip install -r utils/requirements.txt +``` + +## Experiments +**Dataset** +```console +❱❱❱ python create_data.py +``` +use create_data_2_1.py if want to run with multiwoz2.1 + +**Zero-shot cross-domain** +```console +❱❱❱ python T5.py --train_batch_size 16 --GPU 8 --except_domain ${domain} --slot_lang ${description type} +``` +* --GPU: the number of gpu to use +* --except_domain: hold out domain, choose one from [hotel, train, attraction, restaurant, taxi] +* --slot_lang: slot description type, choose one from [none, human, naive, value, question, slottype] + +**Few-shot cross-domain** +```console +❱❱❱ python T5.py --train_batch_size 16 --GPU 8 --slot_lang slottype --model_checkpoint ${checkpoint} --n_epochs 15 --fewshot 0.01 --mode finetune +``` +* --model_checkpoint: saved checkpoint of zero-shot model +* --fewshot: ratio of in-domain data, choose one from [0.01, 0.05, 0.1] + +**Full-shot** +```console +❱❱❱ python T5.py --train_batch_size 16 --GPU 8 --slot_lang slottype --n_epochs 15 +``` diff --git a/T5DST/T5.py b/T5DST/T5.py new file mode 100644 index 0000000..134a054 --- /dev/null +++ b/T5DST/T5.py @@ -0,0 +1,247 @@ +# Copyright (c) Facebook, Inc. and its affiliates + +import os, random +import torch +import argparse +import pytorch_lightning as pl +from pytorch_lightning import Trainer, seed_everything +from transformers import (AdamW, T5Tokenizer, BartTokenizer, BartForConditionalGeneration, T5ForConditionalGeneration, WEIGHTS_NAME,CONFIG_NAME) +from data_loader import prepare_data +from config import get_args +from evaluate import evaluate_metrics +import json +from tqdm import tqdm +from copy import deepcopy +import numpy as np +from collections import Counter + +# def consistency_cross_entropy(lm_logits1, lm_logits2, threshold=0.4): +# logsoftmax = torch.nn.LogSoftmax(dim=1) +# softmax = torch.nn.Softmax(dim=1) + +# lm_logits1 = lm_logits1.squeeze() +# lm_logits2 = lm_logits2.squeeze() +# # (batch, vocab_size) +# # give threshold +# prob2 = softmax(lm_logits2) +# # the result tuple of two output tensors (max, max_indices) +# # print(torch.max(prob2, dim=1)) +# prob2_max, prob2_index = torch.max(prob2, dim=1) +# valid = [] +# for i in range(prob2_max.shape[0]): +# if (prob2_index[i]==5839 and prob2_max[i]>0.9) or (prob2_index[i]!=5839 and prob2_max[i]>threshold): +# valid.append(1) +# else: +# valid.append(0) + +# #sharpening +# soft_targets = softmax(lm_logits2/0.5) + +# loss_temp = torch.sum(- soft_targets * logsoftmax(lm_logits1), 1) +# for i in range(prob2_max.shape[0]): +# if valid[i]==0: +# loss_temp[i]=0 + +# return torch.mean(loss_temp) + + + +class DST_Seq2Seq(pl.LightningModule): + + def __init__(self,args, tokenizer, model): + super().__init__() + self.tokenizer = tokenizer + self.model = model + self.lr = args["lr"] + + + def training_step(self, batch, batch_idx): + self.model.train() + (loss), *_ = self.model(input_ids=batch["encoder_input"], + attention_mask=batch["attention_mask"], + lm_labels=batch["decoder_output"] + ) + + # result = pl.TrainResult(loss) + # result.log('train_loss', loss, on_epoch=True) + return {'loss': loss, 'log': {'train_loss': loss}} + # return result + + def validation_step(self, batch, batch_idx): + self.model.eval() + (loss), *_ = self.model(input_ids=batch["encoder_input"], + attention_mask=batch["attention_mask"], + lm_labels=batch["decoder_output"] + ) + + + return {'val_loss': loss, 'log': {'val_loss': loss}} + # return result + + def validation_epoch_end(self, outputs): + val_loss_mean = sum([o['val_loss'] for o in outputs]) / len(outputs) + # show val_loss in progress bar but only log val_loss + results = {'progress_bar': {'val_loss': val_loss_mean.item()}, 'log': {'val_loss': val_loss_mean.item()}, + 'val_loss': val_loss_mean.item()} + return results + + def configure_optimizers(self): + return AdamW(self.parameters(), lr=self.lr, correct_bias=True) + + + +def train(args, *more): + args = vars(args) + args["model_name"] = args["model_checkpoint"]+args["model_name"]+"_except_domain_"+args["except_domain"]+ "_slotlang_" +str(args["slot_lang"]) + "_lr_" +str(args["lr"]) + "_epoch_" + str(args["n_epochs"]) + "_seed_" + str(args["seed"]) + # train! + seed_everything(args["seed"]) + + + if "t5" in args["model_name"]: + model = T5ForConditionalGeneration.from_pretrained(args["model_checkpoint"]) + tokenizer = T5Tokenizer.from_pretrained(args["model_checkpoint"], bos_token="[bos]", eos_token="[eos]", sep_token="[sep]") + model.resize_token_embeddings(new_num_tokens=len(tokenizer)) + elif "bart" in args["model_name"]: + model = BartForConditionalGeneration.from_pretrained(args["model_checkpoint"]) + tokenizer = BartTokenizer.from_pretrained(args["model_checkpoint"], bos_token="[bos]", eos_token="[eos]", sep_token="[sep]") + model.resize_token_embeddings(new_num_tokens=len(tokenizer)) + + task = DST_Seq2Seq(args, tokenizer, model) + + train_loader, val_loader, test_loader, ALL_SLOTS, fewshot_loader_dev, fewshot_loader_test = prepare_data(args, task.tokenizer) + + #save model path + save_path = os.path.join(args["saving_dir"],args["model_name"]) + if not os.path.exists(save_path): + os.makedirs(save_path) + + trainer = Trainer( + default_root_dir=save_path, + accumulate_grad_batches=args["gradient_accumulation_steps"], + gradient_clip_val=args["max_norm"], + max_epochs=args["n_epochs"], + callbacks=[pl.callbacks.EarlyStopping(monitor='val_loss',min_delta=0.00, patience=5,verbose=False, mode='min')], + gpus=args["GPU"], + deterministic=True, + num_nodes=1, + #precision=16, + accelerator="ddp" + ) + + trainer.fit(task, train_loader, val_loader) + + task.model.save_pretrained(save_path) + task.tokenizer.save_pretrained(save_path) + + print("test start...") + #evaluate model + _ = evaluate_model(args, task.tokenizer, task.model, test_loader, save_path, ALL_SLOTS) + +def evaluate_model(args, tokenizer, model, test_loader, save_path, ALL_SLOTS, prefix="zeroshot"): + save_path = os.path.join(save_path,"results") + if not os.path.exists(save_path): + os.makedirs(save_path) + predictions = {} + # to gpu + # gpu = args["GPU"][0] + device = torch.device("cuda:0") + model.to(device) + model.eval() + + slot_logger = {slot_name:[0,0,0] for slot_name in ALL_SLOTS} + + for batch in tqdm(test_loader): + dst_outputs = model.generate(input_ids=batch["encoder_input"].to(device), + attention_mask=batch["attention_mask"].to(device), + eos_token_id=tokenizer.eos_token_id, + max_length=200, + ) + + value_batch = tokenizer.batch_decode(dst_outputs, skip_special_tokens=True) + + for idx, value in enumerate(value_batch): + dial_id = batch["ID"][idx] + if dial_id not in predictions: + predictions[dial_id] = {} + predictions[dial_id]["domain"] = batch["domains"][idx][0] + predictions[dial_id]["turns"] = {} + if batch["turn_id"][idx] not in predictions[dial_id]["turns"]: + predictions[dial_id]["turns"][batch["turn_id"][idx]] = {"turn_belief":batch["turn_belief"][idx], "pred_belief":[]} + + if value!="none": + predictions[dial_id]["turns"][batch["turn_id"][idx]]["pred_belief"].append(str(batch["slot_text"][idx])+'-'+str(value)) + + # analyze slot acc: + if str(value)==str(batch["value_text"][idx]): + slot_logger[str(batch["slot_text"][idx])][1]+=1 # hit + slot_logger[str(batch["slot_text"][idx])][0]+=1 # total + + for slot_log in slot_logger.values(): + slot_log[2] = slot_log[1]/slot_log[0] + + with open(os.path.join(save_path, f"{prefix}_slot_acc.json"), 'w') as f: + json.dump(slot_logger,f, indent=4) + + with open(os.path.join(save_path, f"{prefix}_prediction.json"), 'w') as f: + json.dump(predictions,f, indent=4) + + joint_acc_score, F1_score, turn_acc_score = evaluate_metrics(predictions, ALL_SLOTS) + + evaluation_metrics = {"Joint Acc":joint_acc_score, "Turn Acc":turn_acc_score, "Joint F1":F1_score} + print(f"{prefix} result:",evaluation_metrics) + + with open(os.path.join(save_path, f"{prefix}_result.json"), 'w') as f: + json.dump(evaluation_metrics,f, indent=4) + + return predictions + + +def fine_tune(args, *more): + args = vars(args) + seed_everything(args["seed"]) + domains = ["hotel", "train", "restaurant", "attraction", "taxi"] + for domain in domains: + if domain in args["model_checkpoint"]: + args["only_domain"] = domain + assert args["only_domain"]!="none" + # args["model_checkpoint"] = os.path.join(args["saving_dir"],args["model_name"]) + print(args) + + if "t5" in args["model_name"]: + model = T5ForConditionalGeneration.from_pretrained(args["model_checkpoint"]) + tokenizer = T5Tokenizer.from_pretrained(args["model_checkpoint"], bos_token="[bos]", eos_token="[eos]", sep_token="[sep]") + elif "bart" in args["model_name"]: + model = BartForConditionalGeneration.from_pretrained(args["model_checkpoint"]) + tokenizer = BartTokenizer.from_pretrained(args["model_checkpoint"], bos_token="[bos]", eos_token="[eos]", sep_token="[sep]") + + task = DST_Seq2Seq(args, tokenizer, model) + train_loader, val_loader, test_loader, ALL_SLOTS, fewshot_loader_dev, fewshot_loader_test = prepare_data(args, tokenizer) + + trainer = Trainer( + default_root_dir=args["model_checkpoint"], + accumulate_grad_batches=args["gradient_accumulation_steps"], + gradient_clip_val=args["max_norm"], + max_epochs=20, + callbacks=[pl.callbacks.EarlyStopping(monitor='val_loss',min_delta=0.00, patience=8,verbose=False, mode='min')], + gpus=args["GPU"], + deterministic=True, + num_nodes=1, + # precision=16, + accelerator="ddp" + ) + + trainer.fit(task, train_loader, val_loader) + + print("test start...") + #evaluate model + ratio = "ratio_" + str(args["fewshot"]) + "_seed_" + str(args["seed"]) + _ = evaluate_model(args, task.tokenizer, task.model, test_loader, args["model_checkpoint"], ALL_SLOTS, prefix=ratio) + + + +if __name__ == "__main__": + args = get_args() + if args.mode=="train": + train(args) + if args.mode=="finetune": + fine_tune(args) diff --git a/T5DST/config.py b/T5DST/config.py new file mode 100644 index 0000000..ae0cf8c --- /dev/null +++ b/T5DST/config.py @@ -0,0 +1,33 @@ +# Copyright (c) Facebook, Inc. and its affiliates + +import argparse + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--model_checkpoint", type=str, default="t5-small", help="Path, url or short name of the model") + parser.add_argument("--saving_dir", type=str, default="save", help="Path for saving") + parser.add_argument("--train_batch_size", type=int, default=16, help="Batch size for training") + parser.add_argument("--meta_batch_size", type=int, default=1, help="Batch size for meta training") + parser.add_argument("--dev_batch_size", type=int, default=8, help="Batch size for validation") + parser.add_argument("--test_batch_size", type=int, default=8, help="Batch size for test") + parser.add_argument("--gradient_accumulation_steps", type=int, default=8, help="Accumulate gradients on several steps") + parser.add_argument("--lr", type=float, default=1e-4, help="Learning rate") + parser.add_argument("--max_norm", type=float, default=1.0, help="Clipping gradient norm") + parser.add_argument("--n_epochs", type=int, default=5, help="Number of training epochs") + parser.add_argument("--seed", type=int, default=557, help="Random seed") + parser.add_argument("--verbose", action='store_true', help="continual baseline") + parser.add_argument("--length", type=int, default=50, help="Batch size for validation") + parser.add_argument("--max_history", type=int, default=2, help="max number of turns in the dialogue") + parser.add_argument("--GPU", type=int, default=8, help="number of gpu to use") + parser.add_argument("--model_name", type=str, default="t5", help="use t5 or bart?") + parser.add_argument("--slot_lang", type=str, default="none", help="use 'none', 'human', 'naive', 'value', 'question', 'slottype' slot description") + parser.add_argument("--fewshot", type=float, default=0.0, help="data ratio for few shot experiment") + parser.add_argument("--fix_label", action='store_true') + parser.add_argument("--except_domain", type=str, default="none", help="hotel, train, restaurant, attraction, taxi") + parser.add_argument("--only_domain", type=str, default="none", help="hotel, train, restaurant, attraction, taxi") + parser.add_argument("--threshold", type=float, default=0.4) + parser.add_argument("--semi", action='store_true') + parser.add_argument("--mode", type=str, default="train") + + args = parser.parse_args() + return args diff --git a/T5DST/create_data.py b/T5DST/create_data.py new file mode 100644 index 0000000..e5d5709 --- /dev/null +++ b/T5DST/create_data.py @@ -0,0 +1,524 @@ +# Copyright (c) Facebook, Inc. and its affiliates + +# -*- coding: utf-8 -*- +import copy +import json +import os +import re +import shutil +import urllib.request +from collections import OrderedDict +from io import BytesIO +from zipfile import ZipFile +import difflib +import numpy as np + +np.set_printoptions(precision=3) + +np.random.seed(2) + + +''' +Most of the codes are from https://github.com/budzianowski/multiwoz +''' + + +# GLOBAL VARIABLES +DICT_SIZE = 400 +MAX_LENGTH = 50 +IGNORE_KEYS_IN_GOAL = ['eod', 'topic', 'messageLen', 'message'] + +fin = open('utils/mapping.pair','r') +replacements = [] +for line in fin.readlines(): + tok_from, tok_to = line.replace('\n', '').split('\t') + replacements.append((' ' + tok_from + ' ', ' ' + tok_to + ' ')) + + + +def clean_value(): + value_map = {["moderate -ly", ]} + +def is_ascii(s): + return all(ord(c) < 128 for c in s) + +def insertSpace(token, text): + sidx = 0 + while True: + sidx = text.find(token, sidx) + if sidx == -1: + break + if sidx + 1 < len(text) and re.match('[0-9]', text[sidx - 1]) and \ + re.match('[0-9]', text[sidx + 1]): + sidx += 1 + continue + if text[sidx - 1] != ' ': + text = text[:sidx] + ' ' + text[sidx:] + sidx += 1 + if sidx + len(token) < len(text) and text[sidx + len(token)] != ' ': + text = text[:sidx + 1] + ' ' + text[sidx + 1:] + sidx += 1 + return text + +def normalize(text, clean_value=True): + # lower case every word + text = text.lower() + + # replace white spaces in front and end + text = re.sub(r'^\s*|\s*$', '', text) + + # hotel domain pfb30 + text = re.sub(r"b&b", "bed and breakfast", text) + text = re.sub(r"b and b", "bed and breakfast", text) + + if clean_value: + # normalize phone number + ms = re.findall('\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4,5})', text) + if ms: + sidx = 0 + for m in ms: + sidx = text.find(m[0], sidx) + if text[sidx - 1] == '(': + sidx -= 1 + eidx = text.find(m[-1], sidx) + len(m[-1]) + text = text.replace(text[sidx:eidx], ''.join(m)) + + # normalize postcode + ms = re.findall('([a-z]{1}[\. ]?[a-z]{1}[\. ]?\d{1,2}[, ]+\d{1}[\. ]?[a-z]{1}[\. ]?[a-z]{1}|[a-z]{2}\d{2}[a-z]{2})', + text) + if ms: + sidx = 0 + for m in ms: + sidx = text.find(m, sidx) + eidx = sidx + len(m) + text = text[:sidx] + re.sub('[,\. ]', '', m) + text[eidx:] + + # weird unicode bug + text = re.sub(u"(\u2018|\u2019)", "'", text) + + if clean_value: + # replace time and and price + text = re.sub(timepat, ' [value_time] ', text) + text = re.sub(pricepat, ' [value_price] ', text) + #text = re.sub(pricepat2, '[value_price]', text) + + # replace st. + text = text.replace(';', ',') + text = re.sub('$\/', '', text) + text = text.replace('/', ' and ') + + # replace other special characters + text = text.replace('-', ' ') + text = re.sub('[\"\<>@\(\)]', '', text) # remove + + # insert white space before and after tokens: + for token in ['?', '.', ',', '!']: + text = insertSpace(token, text) + + # insert white space for 's + text = insertSpace('\'s', text) + + # replace it's, does't, you'd ... etc + text = re.sub('^\'', '', text) + text = re.sub('\'$', '', text) + text = re.sub('\'\s', ' ', text) + text = re.sub('\s\'', ' ', text) + for fromx, tox in replacements: + text = ' ' + text + ' ' + text = text.replace(fromx, tox)[1:-1] + + # remove multiple spaces + text = re.sub(' +', ' ', text) + + # concatenate numbers + tmp = text + tokens = text.split() + i = 1 + while i < len(tokens): + if re.match(u'^\d+$', tokens[i]) and \ + re.match(u'\d+$', tokens[i - 1]): + tokens[i - 1] += tokens[i] + del tokens[i] + else: + i += 1 + text = ' '.join(tokens) + + return text + +def fixDelex(filename, data, data2, idx, idx_acts): + """Given system dialogue acts fix automatic delexicalization.""" + try: + turn = data2[filename.strip('.json')][str(idx_acts)] + except: + return data + + if not isinstance(turn, str):# and not isinstance(turn, unicode): + for k, act in turn.items(): + if 'Attraction' in k: + if 'restaurant_' in data['log'][idx]['text']: + data['log'][idx]['text'] = data['log'][idx]['text'].replace("restaurant", "attraction") + if 'hotel_' in data['log'][idx]['text']: + data['log'][idx]['text'] = data['log'][idx]['text'].replace("hotel", "attraction") + if 'Hotel' in k: + if 'attraction_' in data['log'][idx]['text']: + data['log'][idx]['text'] = data['log'][idx]['text'].replace("attraction", "hotel") + if 'restaurant_' in data['log'][idx]['text']: + data['log'][idx]['text'] = data['log'][idx]['text'].replace("restaurant", "hotel") + if 'Restaurant' in k: + if 'attraction_' in data['log'][idx]['text']: + data['log'][idx]['text'] = data['log'][idx]['text'].replace("attraction", "restaurant") + if 'hotel_' in data['log'][idx]['text']: + data['log'][idx]['text'] = data['log'][idx]['text'].replace("hotel", "restaurant") + + return data + + +def getDialogueAct(filename, data, data2, idx, idx_acts): + """Given system dialogue acts fix automatic delexicalization.""" + acts = [] + try: + turn = data2[filename.strip('.json')][str(idx_acts)] + except: + return acts + + if not isinstance(turn, str): # and not isinstance(turn, unicode): + for k in turn.keys(): + + if k.split('-')[1].lower() == 'request': + for a in turn[k]: + acts.append(a[0].lower()) + elif k.split('-')[1].lower() == 'inform': + for a in turn[k]: + acts.append([a[0].lower(), normalize(a[1].lower(), False)]) + + return acts + + +def get_summary_bstate(bstate, get_domain=False): + """Based on the mturk annotations we form multi-domain belief state""" + domains = [u'taxi',u'restaurant', u'hospital', u'hotel',u'attraction', u'train', u'police'] + summary_bstate = [] + summary_bvalue = [] + active_domain = [] + for domain in domains: + domain_active = False + + booking = [] + #print(domain,len(bstate[domain]['book'].keys())) + for slot in sorted(bstate[domain]['book'].keys()): + if slot == 'booked': + if len(bstate[domain]['book']['booked'])!=0: + booking.append(1) + # summary_bvalue.append("book {} {}:{}".format(domain, slot, "Yes")) + else: + booking.append(0) + else: + if bstate[domain]['book'][slot] != "": + booking.append(1) + summary_bvalue.append(["{}-book {}".format(domain, slot.strip().lower()), normalize(bstate[domain]['book'][slot].strip().lower(), False)]) #(["book", domain, slot, bstate[domain]['book'][slot]]) + else: + booking.append(0) + if domain == 'train': + if 'people' not in bstate[domain]['book'].keys(): + booking.append(0) + if 'ticket' not in bstate[domain]['book'].keys(): + booking.append(0) + summary_bstate += booking + + for slot in bstate[domain]['semi']: + slot_enc = [0, 0, 0] # not mentioned, dontcare, filled + if bstate[domain]['semi'][slot] == 'not mentioned': + slot_enc[0] = 1 + elif bstate[domain]['semi'][slot] in ['dont care', 'dontcare', "don't care", "do not care"]: + slot_enc[1] = 1 + summary_bvalue.append(["{}-{}".format(domain, slot.strip().lower()), "dontcare"]) #(["semi", domain, slot, "dontcare"]) + elif bstate[domain]['semi'][slot]: + summary_bvalue.append(["{}-{}".format(domain, slot.strip().lower()), normalize(bstate[domain]['semi'][slot].strip().lower(), False)]) #(["semi", domain, slot, bstate[domain]['semi'][slot]]) + if slot_enc != [0, 0, 0]: + domain_active = True + summary_bstate += slot_enc + + # quasi domain-tracker + if domain_active: + summary_bstate += [1] + active_domain.append(domain) + else: + summary_bstate += [0] + + #print(len(summary_bstate)) + assert len(summary_bstate) == 94 + if get_domain: + return active_domain + else: + return summary_bstate, summary_bvalue + + +def analyze_dialogue(dialogue, maxlen): + """Cleaning procedure for all kinds of errors in text and annotation.""" + d = dialogue + # do all the necessary postprocessing + if len(d['log']) % 2 != 0: + #print path + print('odd # of turns') + return None # odd number of turns, wrong dialogue + d_pp = {} + d_pp['goal'] = d['goal'] # for now we just copy the goal + usr_turns = [] + sys_turns = [] + # last_bvs = [] + for i in range(len(d['log'])): + if len(d['log'][i]['text'].split()) > maxlen: + # print('too long') + return None # too long sentence, wrong dialogue + if i % 2 == 0: # usr turn + text = d['log'][i]['text'] + if not is_ascii(text): + # print('not ascii') + return None + usr_turns.append(d['log'][i]) + else: # sys turn + text = d['log'][i]['text'] + if not is_ascii(text): + # print('not ascii') + return None + belief_summary, belief_value_summary = get_summary_bstate(d['log'][i]['metadata']) + d['log'][i]['belief_summary'] = str(belief_summary) + d['log'][i]['belief_value_summary'] = belief_value_summary + sys_turns.append(d['log'][i]) + d_pp['usr_log'] = usr_turns + d_pp['sys_log'] = sys_turns + + return d_pp + + +def get_dial(dialogue): + """Extract a dialogue from the file""" + dial = [] + d_orig = analyze_dialogue(dialogue, MAX_LENGTH) # max turn len is 50 words + if d_orig is None: + return None + usr = [t['text'] for t in d_orig['usr_log']] + sys = [t['text'] for t in d_orig['sys_log']] + sys_a = [t['dialogue_acts'] for t in d_orig['sys_log']] + bvs = [t['belief_value_summary'] for t in d_orig['sys_log']] + domain = [t['domain'] for t in d_orig['usr_log']] + for item in zip(usr, sys, sys_a, domain, bvs): + dial.append({'usr':item[0],'sys':item[1], 'sys_a':item[2], 'domain':item[3], 'bvs':item[4]}) + return dial + + +def loadData(): + data_url = "data/multi-woz/data.json" + dataset_url = "https://www.repository.cam.ac.uk/bitstream/handle/1810/280608/MULTIWOZ2.zip?sequence=3&isAllowed=y" + if not os.path.exists("data"): + os.makedirs("data") + os.makedirs("data/multi-woz") + + if not os.path.exists(data_url): + print("Downloading and unzipping the MultiWOZ dataset") + resp = urllib.request.urlopen(dataset_url) + zip_ref = ZipFile(BytesIO(resp.read())) + zip_ref.extractall("data/multi-woz") + zip_ref.close() + shutil.copy('data/multi-woz/MULTIWOZ2 2/data.json', 'data/multi-woz/') + shutil.copy('data/multi-woz/MULTIWOZ2 2/valListFile.json', 'data/multi-woz/') + shutil.copy('data/multi-woz/MULTIWOZ2 2/testListFile.json', 'data/multi-woz/') + shutil.copy('data/multi-woz/MULTIWOZ2 2/dialogue_acts.json', 'data/multi-woz/') + + +def getDomain(idx, log, domains, last_domain): + if idx == 1: + active_domains = get_summary_bstate(log[idx]["metadata"], True) + crnt_doms = active_domains[0] if len(active_domains)!=0 else domains[0] + return crnt_doms + else: + ds_diff = get_ds_diff(log[idx-2]["metadata"], log[idx]["metadata"]) + if len(ds_diff.keys()) == 0: # no clues from dialog states + crnt_doms = last_domain + else: + crnt_doms = list(ds_diff.keys()) + # print(crnt_doms) + return crnt_doms[0] # How about multiple domains in one sentence senario ? + + +def get_ds_diff(prev_d, crnt_d): + diff = {} + # Sometimes, metadata is an empty dictionary, bug? + if not prev_d or not crnt_d: + return diff + + for ((k1, v1), (k2, v2)) in zip(prev_d.items(), crnt_d.items()): + assert k1 == k2 + if v1 != v2: # updated + diff[k2] = v2 + return diff + + +def createData(): + # download the data + loadData() + + # create dictionary of delexicalied values that then we will search against, order matters here! + # dic = delexicalize.prepareSlotValuesIndependent() + delex_data = {} + + fin1 = open('data/multi-woz/data.json', 'r') + data = json.load(fin1) + + fin2 = open('data/multi-woz/dialogue_acts.json', 'r') + data2 = json.load(fin2) + + for didx, dialogue_name in enumerate(data): + + dialogue = data[dialogue_name] + + domains = [] + for dom_k, dom_v in dialogue['goal'].items(): + if dom_v and dom_k not in IGNORE_KEYS_IN_GOAL: # check whether contains some goal entities + domains.append(dom_k) + + idx_acts = 1 + last_domain, last_slot_fill = "", [] + for idx, turn in enumerate(dialogue['log']): + # normalization, split and delexicalization of the sentence + origin_text = normalize(turn['text'], False) + # origin_text = delexicalize.markEntity(origin_text, dic) + dialogue['log'][idx]['text'] = origin_text + + if idx % 2 == 1: # if it's a system turn + + cur_domain = getDomain(idx, dialogue['log'], domains, last_domain) + last_domain = [cur_domain] + + dialogue['log'][idx - 1]['domain'] = cur_domain + dialogue['log'][idx]['dialogue_acts'] = getDialogueAct(dialogue_name, dialogue, data2, idx, idx_acts) + idx_acts += 1 + + # FIXING delexicalization: + dialogue = fixDelex(dialogue_name, dialogue, data2, idx, idx_acts) + + delex_data[dialogue_name] = dialogue + + # if didx > 10: + # break + + # with open('data/multi-woz/woz2like_data.json', 'w') as outfile: + # json.dump(delex_data, outfile) + + return delex_data + + +def buildDelexDict(origin_sent, delex_sent): + dictionary = {} + s = difflib.SequenceMatcher(None, delex_sent.split(), origin_sent.split()) + bs = s.get_matching_blocks() + for i, b in enumerate(bs): + if i < len(bs)-2: + a_start = b.a + b.size + b_start = b.b + b.size + b_end = bs[i+1].b + dictionary[a_start] = " ".join(origin_sent.split()[b_start:b_end]) + return dictionary + + +def divideData(data): + """Given test and validation sets, divide + the data for three different sets""" + testListFile = [] + fin = open('data/multi-woz/testListFile.json', 'r') + for line in fin: + testListFile.append(line[:-1]) + fin.close() + + valListFile = [] + fin = open('data/multi-woz/valListFile.json', 'r') + for line in fin: + valListFile.append(line[:-1]) + fin.close() + + trainListFile = open('data/trainListFile', 'w') + + test_dials = [] + val_dials = [] + train_dials = [] + + # dictionaries + word_freqs_usr = OrderedDict() + word_freqs_sys = OrderedDict() + + count_train, count_val, count_test = 0, 0, 0 + + ontology = {} + + for dialogue_name in data: + # print dialogue_name + dial_item = data[dialogue_name] + domains = [] + for dom_k, dom_v in dial_item['goal'].items(): + if dom_v and dom_k not in IGNORE_KEYS_IN_GOAL: # check whether contains some goal entities + domains.append(dom_k) + + turn_exmaple = {"system":"none", "user":"none", "state":{"active_intent":"none", "slot_values":{} } } + dial = get_dial(data[dialogue_name]) + if dial: + dial_example = {"dial_id":dialogue_name, "domains":list(set(domains)) ,"turns":[]} + # dialogue = {} + # dialogue['dialogue_idx'] = dialogue_name + # dialogue['domains'] = list(set(domains)) #list(set([d['domain'] for d in dial])) + # last_bs = [] + # dialogue['dialogue'] = [] + + for turn_i, turn in enumerate(dial): + # usr, usr_o, sys, sys_o, sys_a, domain + turn_exmaple = {"system":"none", "user":"none", "state":{"active_intent":"none", "slot_values":{} } } + turn_exmaple['system'] = dial[turn_i-1]['sys'] if turn_i > 0 else "none" + turn_exmaple['state']["slot_values"] = {s[0]:s[1] for s in turn['bvs']} + turn_exmaple['user'] = turn['usr'] + dial_example['turns'].append(turn_exmaple) + + for ss, vv in turn_exmaple['state']["slot_values"].items(): + if ss not in ontology: + ontology[ss] = [] + if vv not in ontology[ss]: + ontology[ss].append(vv) + + if dialogue_name in testListFile: + test_dials.append(dial_example) + count_test += 1 + elif dialogue_name in valListFile: + val_dials.append(dial_example) + count_val += 1 + else: + trainListFile.write(dialogue_name + '\n') + train_dials.append(dial_example) + count_train += 1 + + print("# of dialogues: Train {}, Val {}, Test {}".format(count_train, count_val, count_test)) + + # save all dialogues + with open('data/dev_dials.json', 'w') as f: + json.dump(val_dials, f, indent=4) + + with open('data/test_dials.json', 'w') as f: + json.dump(test_dials, f, indent=4) + + with open('data/train_dials.json', 'w') as f: + json.dump(train_dials, f, indent=4) + + with open('data/ontology.json', 'w') as f: + json.dump(ontology, f, indent=4) + + # return word_freqs_usr, word_freqs_sys + + +def main(): + print('Create WOZ-like dialogues. Get yourself a coffee, this might take a while.') + delex_data = createData() + print('Divide dialogues...') + divideData(delex_data) + # print('Building dictionaries') + # buildDictionaries(word_freqs_usr, word_freqs_sys) + + +if __name__ == "__main__": + main() diff --git a/T5DST/create_data_2_1.py b/T5DST/create_data_2_1.py new file mode 100644 index 0000000..39078a5 --- /dev/null +++ b/T5DST/create_data_2_1.py @@ -0,0 +1,534 @@ +# Copyright (c) Facebook, Inc. and its affiliates +# -*- coding: utf-8 -*- +import copy +import json +import os +import re +import shutil +import urllib.request +from collections import OrderedDict +from io import BytesIO +from zipfile import ZipFile +import difflib +import numpy as np +import argparse +from shutil import copyfile + +np.set_printoptions(precision=3) + +np.random.seed(2) + + +''' +Most of the codes are from https://github.com/budzianowski/multiwoz +''' + + +# GLOBAL VARIABLES +DICT_SIZE = 400 +MAX_LENGTH = 50 +IGNORE_KEYS_IN_GOAL = ['eod', 'topic', 'messageLen', 'message'] + +fin = open('utils/mapping.pair','r') +replacements = [] +for line in fin.readlines(): + tok_from, tok_to = line.replace('\n', '').split('\t') + replacements.append((' ' + tok_from + ' ', ' ' + tok_to + ' ')) + + +def is_ascii(s): + return all(ord(c) < 128 for c in s) + +def insertSpace(token, text): + sidx = 0 + while True: + sidx = text.find(token, sidx) + if sidx == -1: + break + if sidx + 1 < len(text) and re.match('[0-9]', text[sidx - 1]) and \ + re.match('[0-9]', text[sidx + 1]): + sidx += 1 + continue + if text[sidx - 1] != ' ': + text = text[:sidx] + ' ' + text[sidx:] + sidx += 1 + if sidx + len(token) < len(text) and text[sidx + len(token)] != ' ': + text = text[:sidx + 1] + ' ' + text[sidx + 1:] + sidx += 1 + return text + +def normalize(text, clean_value=True): + # lower case every word + text = text.lower() + + # replace white spaces in front and end + text = re.sub(r'^\s*|\s*$', '', text) + + # hotel domain pfb30 + text = re.sub(r"b&b", "bed and breakfast", text) + text = re.sub(r"b and b", "bed and breakfast", text) + + if clean_value: + # normalize phone number + ms = re.findall('\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4,5})', text) + if ms: + sidx = 0 + for m in ms: + sidx = text.find(m[0], sidx) + if text[sidx - 1] == '(': + sidx -= 1 + eidx = text.find(m[-1], sidx) + len(m[-1]) + text = text.replace(text[sidx:eidx], ''.join(m)) + + # normalize postcode + ms = re.findall('([a-z]{1}[\. ]?[a-z]{1}[\. ]?\d{1,2}[, ]+\d{1}[\. ]?[a-z]{1}[\. ]?[a-z]{1}|[a-z]{2}\d{2}[a-z]{2})', + text) + if ms: + sidx = 0 + for m in ms: + sidx = text.find(m, sidx) + eidx = sidx + len(m) + text = text[:sidx] + re.sub('[,\. ]', '', m) + text[eidx:] + + # weird unicode bug + text = re.sub(u"(\u2018|\u2019)", "'", text) + + if clean_value: + # replace time and and price + text = re.sub(timepat, ' [value_time] ', text) + text = re.sub(pricepat, ' [value_price] ', text) + #text = re.sub(pricepat2, '[value_price]', text) + + # replace st. + text = text.replace(';', ',') + text = re.sub('$\/', '', text) + text = text.replace('/', ' and ') + + # replace other special characters + text = text.replace('-', ' ') + text = re.sub('[\"\<>@\(\)]', '', text) # remove + + # insert white space before and after tokens: + for token in ['?', '.', ',', '!']: + text = insertSpace(token, text) + + # insert white space for 's + text = insertSpace('\'s', text) + + # replace it's, does't, you'd ... etc + text = re.sub('^\'', '', text) + text = re.sub('\'$', '', text) + text = re.sub('\'\s', ' ', text) + text = re.sub('\s\'', ' ', text) + for fromx, tox in replacements: + text = ' ' + text + ' ' + text = text.replace(fromx, tox)[1:-1] + + # remove multiple spaces + text = re.sub(' +', ' ', text) + + # concatenate numbers + tmp = text + tokens = text.split() + i = 1 + while i < len(tokens): + if re.match(u'^\d+$', tokens[i]) and \ + re.match(u'\d+$', tokens[i - 1]): + tokens[i - 1] += tokens[i] + del tokens[i] + else: + i += 1 + text = ' '.join(tokens) + + return text + +def fixDelex(filename, data, data2, idx, idx_acts): + """Given system dialogue acts fix automatic delexicalization.""" + try: + turn = data2[filename.strip('.json')][str(idx_acts)] + except: + return data + + if not isinstance(turn, str):# and not isinstance(turn, unicode): + for k, act in turn.items(): + if 'Attraction' in k: + if 'restaurant_' in data['log'][idx]['text']: + data['log'][idx]['text'] = data['log'][idx]['text'].replace("restaurant", "attraction") + if 'hotel_' in data['log'][idx]['text']: + data['log'][idx]['text'] = data['log'][idx]['text'].replace("hotel", "attraction") + if 'Hotel' in k: + if 'attraction_' in data['log'][idx]['text']: + data['log'][idx]['text'] = data['log'][idx]['text'].replace("attraction", "hotel") + if 'restaurant_' in data['log'][idx]['text']: + data['log'][idx]['text'] = data['log'][idx]['text'].replace("restaurant", "hotel") + if 'Restaurant' in k: + if 'attraction_' in data['log'][idx]['text']: + data['log'][idx]['text'] = data['log'][idx]['text'].replace("attraction", "restaurant") + if 'hotel_' in data['log'][idx]['text']: + data['log'][idx]['text'] = data['log'][idx]['text'].replace("hotel", "restaurant") + + return data + + +def getDialogueAct(filename, data, data2, idx, idx_acts): + """Given system dialogue acts fix automatic delexicalization.""" + acts = [] + try: + turn = data2[filename.strip('.json')][str(idx_acts)] + except: + return acts + + if not isinstance(turn, str): # and not isinstance(turn, unicode): + for k in turn.keys(): + # temp = [k.split('-')[0].lower(), k.split('-')[1].lower()] + # for a in turn[k]: + # acts.append(temp + [a[0].lower()]) + + if k.split('-')[1].lower() == 'request': + for a in turn[k]: + acts.append(a[0].lower()) + elif k.split('-')[1].lower() == 'inform': + for a in turn[k]: + acts.append([a[0].lower(), normalize(a[1].lower(), False)]) + + return acts + + +def get_summary_bstate(bstate, get_domain=False): + """Based on the mturk annotations we form multi-domain belief state""" + domains = [u'taxi',u'restaurant', u'hospital', u'hotel',u'attraction', u'train', u'police'] + summary_bstate = [] + summary_bvalue = [] + active_domain = [] + for domain in domains: + domain_active = False + + booking = [] + #print(domain,len(bstate[domain]['book'].keys())) + for slot in sorted(bstate[domain]['book'].keys()): + if slot == 'booked': + if len(bstate[domain]['book']['booked'])!=0: + booking.append(1) + # summary_bvalue.append("book {} {}:{}".format(domain, slot, "Yes")) + else: + booking.append(0) + else: + if bstate[domain]['book'][slot] != "": + booking.append(1) + summary_bvalue.append(["{}-book {}".format(domain, slot.strip().lower()), normalize(bstate[domain]['book'][slot].strip().lower(), False)]) #(["book", domain, slot, bstate[domain]['book'][slot]]) + else: + booking.append(0) + if domain == 'train': + if 'people' not in bstate[domain]['book'].keys(): + booking.append(0) + if 'ticket' not in bstate[domain]['book'].keys(): + booking.append(0) + summary_bstate += booking + + for slot in bstate[domain]['semi']: + slot_enc = [0, 0, 0] # not mentioned, dontcare, filled + if bstate[domain]['semi'][slot] == 'not mentioned': + slot_enc[0] = 1 + elif bstate[domain]['semi'][slot] in ['dont care', 'dontcare', "don't care", "do not care"]: + slot_enc[1] = 1 + summary_bvalue.append(["{}-{}".format(domain, slot.strip().lower()), "dontcare"]) #(["semi", domain, slot, "dontcare"]) + elif bstate[domain]['semi'][slot]: + summary_bvalue.append(["{}-{}".format(domain, slot.strip().lower()), normalize(bstate[domain]['semi'][slot].strip().lower(), False)]) #(["semi", domain, slot, bstate[domain]['semi'][slot]]) + if slot_enc != [0, 0, 0]: + domain_active = True + summary_bstate += slot_enc + + # quasi domain-tracker + if domain_active: + summary_bstate += [1] + active_domain.append(domain) + else: + summary_bstate += [0] + + #print(len(summary_bstate)) + assert len(summary_bstate) == 94 + if get_domain: + return active_domain + else: + return summary_bstate, summary_bvalue + + +def analyze_dialogue(dialogue, maxlen): + """Cleaning procedure for all kinds of errors in text and annotation.""" + d = dialogue + # do all the necessary postprocessing + if len(d['log']) % 2 != 0: + #print path + print('odd # of turns') + return None # odd number of turns, wrong dialogue + d_pp = {} + d_pp['goal'] = d['goal'] # for now we just copy the goal + usr_turns = [] + sys_turns = [] + # last_bvs = [] + for i in range(len(d['log'])): + if len(d['log'][i]['text'].split()) > maxlen: + # print('too long') + return None # too long sentence, wrong dialogue + if i % 2 == 0: # usr turn + text = d['log'][i]['text'] + if not is_ascii(text): + # print('not ascii') + return None + usr_turns.append(d['log'][i]) + else: # sys turn + text = d['log'][i]['text'] + if not is_ascii(text): + # print('not ascii') + return None + belief_summary, belief_value_summary = get_summary_bstate(d['log'][i]['metadata']) + d['log'][i]['belief_summary'] = str(belief_summary) + d['log'][i]['belief_value_summary'] = belief_value_summary + sys_turns.append(d['log'][i]) + d_pp['usr_log'] = usr_turns + d_pp['sys_log'] = sys_turns + + return d_pp + + +def get_dial(dialogue): + """Extract a dialogue from the file""" + dial = [] + d_orig = analyze_dialogue(dialogue, MAX_LENGTH) # max turn len is 50 words + if d_orig is None: + return None + usr = [t['text'] for t in d_orig['usr_log']] + sys = [t['text'] for t in d_orig['sys_log']] + sys_a = [t['dialogue_acts'] for t in d_orig['sys_log']] + bvs = [t['belief_value_summary'] for t in d_orig['sys_log']] + domain = [t['domain'] for t in d_orig['usr_log']] + for item in zip(usr, sys, sys_a, domain, bvs): + dial.append({'usr':item[0],'sys':item[1], 'sys_a':item[2], 'domain':item[3], 'bvs':item[4]}) + return dial + + +def loadData(args): + data_url = os.path.join(args.main_dir, "data.json") + if args.mwz_ver == '2.1': + dataset_url = "https://www.repository.cam.ac.uk/bitstream/handle/1810/294507/MULTIWOZ2.1.zip?sequence=1&isAllowed=y" + else: + dataset_url = "https://www.repository.cam.ac.uk/bitstream/handle/1810/280608/MULTIWOZ2.zip?sequence=3&isAllowed=y" + if not os.path.exists(args.main_dir): + os.makedirs(args.main_dir) + + if not os.path.exists(data_url): + print("Downloading and unzipping the MultiWOZ %s dataset" % args.mwz_ver) + resp = urllib.request.urlopen(dataset_url) + zip_ref = ZipFile(BytesIO(resp.read())) + zip_ref.extractall(args.main_dir) + zip_ref.close() + dir_name = 'MULTIWOZ2.1' if args.mwz_ver == '2.1' else 'MULTIWOZ2 2' + shutil.copy(os.path.join(args.main_dir, dir_name, 'data.json'), args.main_dir) + shutil.copy(os.path.join(args.main_dir, dir_name, 'ontology.json'), args.main_dir) + shutil.copy(os.path.join(args.main_dir, dir_name, 'valListFile.json'), args.main_dir) + shutil.copy(os.path.join(args.main_dir, dir_name, 'testListFile.json'), args.main_dir) + shutil.copy(os.path.join(args.main_dir, dir_name, 'dialogue_acts.json'), args.main_dir) + + +def getDomain(idx, log, domains, last_domain): + if idx == 1: + active_domains = get_summary_bstate(log[idx]["metadata"], True) + crnt_doms = active_domains[0] if len(active_domains)!=0 else domains[0] + return crnt_doms + else: + ds_diff = get_ds_diff(log[idx-2]["metadata"], log[idx]["metadata"]) + if len(ds_diff.keys()) == 0: # no clues from dialog states + crnt_doms = last_domain + else: + crnt_doms = list(ds_diff.keys()) + # print(crnt_doms) + return crnt_doms[0] # How about multiple domains in one sentence senario ? + + +def get_ds_diff(prev_d, crnt_d): + diff = {} + # Sometimes, metadata is an empty dictionary, bug? + if not prev_d or not crnt_d: + return diff + + for ((k1, v1), (k2, v2)) in zip(prev_d.items(), crnt_d.items()): + assert k1 == k2 + if v1 != v2: # updated + diff[k2] = v2 + return diff + + +def createData(args): + # download the data + loadData(args) + + # create dictionary of delexicalied values that then we will search against, order matters here! + # dic = delexicalize.prepareSlotValuesIndependent() + delex_data = {} + + fin1 = open(os.path.join(args.main_dir, 'data.json'), 'r') + data = json.load(fin1) + + fin2 = open(os.path.join(args.main_dir, 'dialogue_acts.json'), 'r') + data2 = json.load(fin2) + + for didx, dialogue_name in enumerate(data): + + dialogue = data[dialogue_name] + + domains = [] + for dom_k, dom_v in dialogue['goal'].items(): + if dom_v and dom_k not in IGNORE_KEYS_IN_GOAL: # check whether contains some goal entities + domains.append(dom_k) + + idx_acts = 1 + last_domain, last_slot_fill = "", [] + for idx, turn in enumerate(dialogue['log']): + # normalization, split and delexicalization of the sentence + origin_text = normalize(turn['text'], False) + # origin_text = delexicalize.markEntity(origin_text, dic) + dialogue['log'][idx]['text'] = origin_text + + if idx % 2 == 1: # if it's a system turn + + cur_domain = getDomain(idx, dialogue['log'], domains, last_domain) + last_domain = [cur_domain] + + dialogue['log'][idx - 1]['domain'] = cur_domain + dialogue['log'][idx]['dialogue_acts'] = getDialogueAct(dialogue_name, dialogue, data2, idx, idx_acts) + idx_acts += 1 + + # FIXING delexicalization: + dialogue = fixDelex(dialogue_name, dialogue, data2, idx, idx_acts) + + delex_data[dialogue_name] = dialogue + + # if didx > 10: + # break + + # with open('data/multi-woz/woz2like_data.json', 'w') as outfile: + # json.dump(delex_data, outfile) + + return delex_data + + +def buildDelexDict(origin_sent, delex_sent): + dictionary = {} + s = difflib.SequenceMatcher(None, delex_sent.split(), origin_sent.split()) + bs = s.get_matching_blocks() + for i, b in enumerate(bs): + if i < len(bs)-2: + a_start = b.a + b.size + b_start = b.b + b.size + b_end = bs[i+1].b + dictionary[a_start] = " ".join(origin_sent.split()[b_start:b_end]) + return dictionary + + +def divideData(data,args): + """Given test and validation sets, divide + the data for three different sets""" + os.makedirs(args.target_path,exist_ok=True) + + copyfile(os.path.join(args.main_dir,'ontology.json'), os.path.join(args.target_path,'ontology.json')) + + testListFile = [] + fin = open(os.path.join(args.main_dir,'testListFile.json'), 'r') + for line in fin: + testListFile.append(line[:-1]) + fin.close() + + valListFile = [] + fin = open(os.path.join(args.main_dir,'valListFile.json'), 'r') + for line in fin: + valListFile.append(line[:-1]) + fin.close() + + trainListFile = open(os.path.join(args.target_path,'trainListFile'), 'w') + + test_dials = [] + val_dials = [] + train_dials = [] + + # dictionaries + word_freqs_usr = OrderedDict() + word_freqs_sys = OrderedDict() + + count_train, count_val, count_test = 0, 0, 0 + + ontology = {} + + for dialogue_name in data: + # print dialogue_name + dial_item = data[dialogue_name] + domains = [] + for dom_k, dom_v in dial_item['goal'].items(): + if dom_v and dom_k not in IGNORE_KEYS_IN_GOAL: # check whether contains some goal entities + domains.append(dom_k) + + turn_exmaple = {"system":"none", "user":"none", "state":{"active_intent":"none", "slot_values":{} } } + dial = get_dial(data[dialogue_name]) + if dial: + dial_example = {"dial_id":dialogue_name, "domains":list(set(domains)) ,"turns":[]} + # dialogue = {} + # dialogue['dialogue_idx'] = dialogue_name + # dialogue['domains'] = list(set(domains)) #list(set([d['domain'] for d in dial])) + # last_bs = [] + # dialogue['dialogue'] = [] + + for turn_i, turn in enumerate(dial): + # usr, usr_o, sys, sys_o, sys_a, domain + turn_exmaple = {"system":"none", "user":"none", "state":{"active_intent":"none", "slot_values":{} } } + turn_exmaple['system'] = dial[turn_i-1]['sys'] if turn_i > 0 else "none" + turn_exmaple['state']["slot_values"] = {s[0]:s[1] for s in turn['bvs']} + turn_exmaple['user'] = turn['usr'] + dial_example['turns'].append(turn_exmaple) + + for ss, vv in turn_exmaple['state']["slot_values"].items(): + if ss not in ontology: + ontology[ss] = [] + if vv not in ontology[ss]: + ontology[ss].append(vv) + + if dialogue_name in testListFile: + test_dials.append(dial_example) + count_test += 1 + elif dialogue_name in valListFile: + val_dials.append(dial_example) + count_val += 1 + else: + trainListFile.write(dialogue_name + '\n') + train_dials.append(dial_example) + count_train += 1 + + print("# of dialogues: Train {}, Val {}, Test {}".format(count_train, count_val, count_test)) + + # save all dialogues + with open('data/dev_dials.json', 'w') as f: + json.dump(val_dials, f, indent=4) + + with open('data/test_dials.json', 'w') as f: + json.dump(test_dials, f, indent=4) + + with open('data/train_dials.json', 'w') as f: + json.dump(train_dials, f, indent=4) + + with open('data/ontology.json', 'w') as f: + json.dump(ontology, f, indent=4) + + + +def main(args): + print('Create WOZ-like dialogues. Get yourself a coffee, this might take a while.') + delex_data = createData(args) + print('Divide dialogues...') + divideData(delex_data,args) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--main_dir", type=str, default='data') + parser.add_argument("--mwz_ver", type=str, default='2.1') + parser.add_argument("--target_path", type=str, default='data/mwz2.1') + args = parser.parse_args() + main(args) diff --git a/T5DST/data_loader.py b/T5DST/data_loader.py new file mode 100644 index 0000000..89290ef --- /dev/null +++ b/T5DST/data_loader.py @@ -0,0 +1,244 @@ +# Copyright (c) Facebook, Inc. and its affiliates + +import json +import torch +from torch.utils.data import DataLoader, TensorDataset, Dataset +import ast +from tqdm import tqdm +import os +import random +from functools import partial +from utils.fix_label import fix_general_label_error +from collections import OrderedDict +EXPERIMENT_DOMAINS = ["hotel", "train", "restaurant", "attraction", "taxi"] + +random.seed(577) +HISTORY_MAX_LEN = 450 +GPT_MAX_LEN = 1024 + +class DSTDataset(Dataset): + """Custom data.Dataset compatible with data.DataLoader.""" + def __init__(self, data, args): + """Reads source and target sequences from txt files.""" + self.data = data + self.args = args + + def __getitem__(self, index): + """Returns one data pair (source and target).""" + item_info = self.data[index] + if self.args["slot_lang"] == "value": + random.shuffle(item_info["value_list"]) + item_info["intput_text"] += " is " + " or ".join(item_info["value_list"]) + " or none?" + return item_info + + def __len__(self): + return len(self.data) + + + +def read_data(args, path_name, SLOTS, tokenizer, description, dataset=None): + slot_lang_list = ["description_human", "rule_description", "value_description", "rule2", "rule3"] + print(("Reading all files from {}".format(path_name))) + data = [] + domain_counter = {} + # read files + with open(path_name) as f: + dials = json.load(f) + + if dataset=="train" and args["fewshot"]>0: + random.Random(args["seed"]).shuffle(dials) + dials = dials[:int(len(dials)*args["fewshot"])] + + for dial_dict in dials: + dialog_history = "" + + # Counting domains + for domain in dial_dict["domains"]: + if domain not in EXPERIMENT_DOMAINS: + continue + if domain not in domain_counter.keys(): + domain_counter[domain] = 0 + domain_counter[domain] += 1 + + # Unseen domain setting + if args["only_domain"] != "none" and args["only_domain"] not in dial_dict["domains"]: + continue + if (args["except_domain"] != "none" and dataset == "test" and args["except_domain"] not in dial_dict["domains"]) or \ + (args["except_domain"] != "none" and dataset != "test" and [args["except_domain"]] == dial_dict["domains"]): + continue + + # Reading data + for ti, turn in enumerate(dial_dict["turns"]): + turn_id = ti + + # accumulate dialogue utterances + dialog_history += (" System: " + turn["system"] + " User: " + turn["user"]) + if args["fix_label"]: + slot_values = fix_general_label_error(turn["state"]["slot_values"],SLOTS) + else: + slot_values = turn["state"]["slot_values"] + # input: dialogue history + slot + # output: value + + # Generate domain-dependent slot list + slot_temp = SLOTS + if dataset == "train" or dataset == "dev": + if args["except_domain"] != "none": + slot_temp = [k for k in SLOTS if args["except_domain"] not in k] + slot_values = OrderedDict([(k, v) for k, v in slot_values.items() if args["except_domain"] not in k]) + elif args["only_domain"] != "none": + slot_temp = [k for k in SLOTS if args["only_domain"] in k] + slot_values = OrderedDict([(k, v) for k, v in slot_values.items() if args["only_domain"] in k]) + else: + if args["except_domain"] != "none": + slot_temp = [k for k in SLOTS if args["except_domain"] in k] + slot_values = OrderedDict([(k, v) for k, v in slot_values.items() if args["except_domain"] in k]) + elif args["only_domain"] != "none": + slot_temp = [k for k in SLOTS if args["only_domain"] in k] + slot_values = OrderedDict([(k, v) for k, v in slot_values.items() if args["only_domain"] in k]) + + + turn_belief_list = [str(k)+'-'+str(v) for k,v in slot_values.items()] + + # baseline gpt have different preprocessing, e.g., output: (slot1-value1, slot2-value2, slot3-value3, ...) + if "gpt" in args["model_name"]: + turn_slots = [] + turn_slot_values = [] + if len(dialog_history.split())>800: + continue + for slot in slot_temp: + # skip unrelevant slots for out of domain setting + if args["except_domain"] != "none" and dataset !="test": + if slot.split("-")[0] not in dial_dict["domains"]: + continue + input_text = dialog_history + f" {tokenizer.sep_token} {slot}" + " " + tokenizer.bos_token + output_text = input_text+ " " + turn["state"]["slot_values"].get(slot, 'none').strip() + " " + tokenizer.eos_token + slot_text = slot + value_text = turn["state"]["slot_values"].get(slot, 'none').strip() + + data_detail = { + "ID":dial_dict["dial_id"], + "domains":dial_dict["domains"], + "turn_id":turn_id, + "dialog_history":dialog_history, + "turn_belief":turn_belief_list, + "intput_text":input_text, + "output_text":output_text, + "slot_text":slot_text, + "value_text":value_text + } + data.append(data_detail) + + else: + for slot in slot_temp: + + # skip unrelevant slots for out of domain setting + if args["except_domain"] != "none" and dataset !="test": + if slot.split("-")[0] not in dial_dict["domains"]: + continue + + output_text = slot_values.get(slot, 'none').strip() + f" {tokenizer.eos_token}" + slot_text = slot + value_text = slot_values.get(slot, 'none').strip() + + if args["slot_lang"]=="human": + slot_lang = description[slot]["description_human"] + input_text = dialog_history + f" {tokenizer.sep_token} {slot_lang}?" + elif args["slot_lang"]=="naive": + slot_lang = description[slot]["naive"] + input_text = dialog_history + f" {tokenizer.sep_token} {slot_lang}?" + elif args["slot_lang"]=="value": + slot_lang = description[slot]["naive"] + input_text = dialog_history + f" {tokenizer.sep_token} {slot_lang}" + elif args["slot_lang"]=="question": + slot_lang = description[slot]["question"] + input_text = dialog_history + f" {tokenizer.sep_token} {slot_lang}" + elif args["slot_lang"]=="slottype": + slot_lang = description[slot]["slottype"] + input_text = dialog_history + f" {tokenizer.sep_token} {slot_lang}?" + else: + input_text = dialog_history + f" {tokenizer.sep_token} {slot}" + + data_detail = { + "ID":dial_dict["dial_id"], + "domains":dial_dict["domains"], + "turn_id":turn_id, + "dialog_history":dialog_history, + "turn_belief":turn_belief_list, + "intput_text":input_text, + "output_text":output_text, + "slot_text":slot_text, + "value_text":value_text, + "value_list":description[slot]["values"] + } + data.append(data_detail) + # print(len(data)) + for idx in range(10): + print(data[idx]) + print("domain_counter", domain_counter) + return data, slot_temp + + + +def get_slot_information(ontology): + ontology_domains = dict([(k, v) for k, v in ontology.items() if k.split("-")[0] in EXPERIMENT_DOMAINS]) + SLOTS = [k.replace(" ","").lower() if ("book" not in k) else k.lower() for k in ontology_domains.keys()] + + return SLOTS + + +def gpt_collate_fn(data,tokenizer): + batch_data = {} + for key in data[0]: + batch_data[key] = [d[key] for d in data] + + output_batch = tokenizer(batch_data["output_text"], padding=True, return_tensors="pt", add_special_tokens=False, return_attention_mask=False, truncation=True, max_length=1000) + batch_data["input_ids"] = output_batch['input_ids'] + return batch_data + + +def collate_fn(data, tokenizer): + batch_data = {} + for key in data[0]: + batch_data[key] = [d[key] for d in data] + + input_batch = tokenizer(batch_data["intput_text"], padding=True, return_tensors="pt", add_special_tokens=False, verbose=False) + batch_data["encoder_input"] = input_batch["input_ids"] + batch_data["attention_mask"] = input_batch["attention_mask"] + output_batch = tokenizer(batch_data["output_text"], padding=True, return_tensors="pt", add_special_tokens=False, return_attention_mask=False) + # replace the padding id to -100 for cross-entropy + output_batch['input_ids'].masked_fill_(output_batch['input_ids']==tokenizer.pad_token_id, -100) + batch_data["decoder_output"] = output_batch['input_ids'] + + return batch_data + + +def prepare_data(args, tokenizer): + path_train = 'data/train_dials.json' + path_dev = 'data/dev_dials.json' + path_test = 'data/test_dials.json' + + ontology = json.load(open("data/multi-woz/MULTIWOZ2 2/ontology.json", 'r')) + ALL_SLOTS = get_slot_information(ontology) + description = json.load(open("utils/slot_description.json", 'r')) + + data_train, _ = read_data(args, path_train, ALL_SLOTS, tokenizer, description, "train") + data_dev, _ = read_data(args, path_dev, ALL_SLOTS, tokenizer, description, "dev") + data_test, ALL_SLOTS = read_data(args, path_test, ALL_SLOTS, tokenizer, description, "test") + + + train_dataset = DSTDataset(data_train, args) + dev_dataset = DSTDataset(data_dev, args) + test_dataset = DSTDataset(data_test, args) + + if "gpt" in args["model_name"]: + train_loader = DataLoader(train_dataset, batch_size=args["train_batch_size"], shuffle=True, collate_fn=partial(gpt_collate_fn, tokenizer=tokenizer), num_workers=16) + test_loader = DataLoader(test_dataset, batch_size=args["test_batch_size"], shuffle=False, collate_fn=partial(gpt_collate_fn, tokenizer=tokenizer), num_workers=16) + dev_loader = DataLoader(dev_dataset, batch_size=args["dev_batch_size"], shuffle=False, collate_fn=partial(gpt_collate_fn, tokenizer=tokenizer), num_workers=16) + else: + train_loader = DataLoader(train_dataset, batch_size=args["train_batch_size"], shuffle=True, collate_fn=partial(collate_fn, tokenizer=tokenizer), num_workers=16) + test_loader = DataLoader(test_dataset, batch_size=args["test_batch_size"], shuffle=False, collate_fn=partial(collate_fn, tokenizer=tokenizer), num_workers=16) + dev_loader = DataLoader(dev_dataset, batch_size=args["dev_batch_size"], shuffle=False, collate_fn=partial(collate_fn, tokenizer=tokenizer), num_workers=16) + fewshot_loader_dev=None + fewshot_loader_test=None + return train_loader, dev_loader, test_loader, ALL_SLOTS, fewshot_loader_dev, fewshot_loader_test diff --git a/T5DST/evaluate.py b/T5DST/evaluate.py new file mode 100644 index 0000000..cf0804f --- /dev/null +++ b/T5DST/evaluate.py @@ -0,0 +1,86 @@ +# Copyright (c) Facebook, Inc. and its affiliates + +import json +# Strict match evaluation from https://github.com/jasonwu0731/trade-dst/blob/master/models/TRADE.py +# check utils/prediction_sample.json for the format of predictions +EXPERIMENT_DOMAINS = ["hotel", "train", "restaurant", "attraction", "taxi"] + +def compute_acc(gold, pred, slot_temp): + miss_gold = 0 + miss_slot = [] + for g in gold: + if g not in pred: + miss_gold += 1 + miss_slot.append(g.rsplit("-", 1)[0]) + wrong_pred = 0 + for p in pred: + if p not in gold and p.rsplit("-", 1)[0] not in miss_slot: + wrong_pred += 1 + ACC_TOTAL = len(slot_temp) + ACC = len(slot_temp) - miss_gold - wrong_pred + ACC = ACC / float(ACC_TOTAL) + return ACC + +def compute_prf(gold, pred): + TP, FP, FN = 0, 0, 0 + if len(gold)!= 0: + count = 1 + for g in gold: + if g in pred: + TP += 1 + else: + FN += 1 + for p in pred: + if p not in gold: + FP += 1 + precision = TP / float(TP+FP) if (TP+FP)!=0 else 0 + recall = TP / float(TP+FN) if (TP+FN)!=0 else 0 + F1 = 2 * precision * recall / float(precision + recall) if (precision+recall)!=0 else 0 + else: + if len(pred)==0: + precision, recall, F1, count = 1, 1, 1, 1 + else: + precision, recall, F1, count = 0, 0, 0, 1 + return F1, recall, precision, count + +def evaluate_metrics(all_prediction, SLOT_LIST): + total, turn_acc, joint_acc, F1_pred, F1_count = 0, 0, 0, 0, 0 + for idx, dial in all_prediction.items(): + for k, cv in dial["turns"].items(): + if set(cv["turn_belief"]) == set(cv["pred_belief"]): + joint_acc += 1 + else: + print(cv["turn_belief"]) + print(cv["pred_belief"]) + print("==================") + total += 1 + + # Compute prediction slot accuracy + temp_acc = compute_acc(set(cv["turn_belief"]), set(cv["pred_belief"]), SLOT_LIST) + turn_acc += temp_acc + + # Compute prediction joint F1 score + temp_f1, temp_r, temp_p, count = compute_prf(set(cv["turn_belief"]), set(cv["pred_belief"])) + F1_pred += temp_f1 + F1_count += count + + joint_acc_score = joint_acc / float(total) if total!=0 else 0 + turn_acc_score = turn_acc / float(total) if total!=0 else 0 + F1_score = F1_pred / float(F1_count) if F1_count!=0 else 0 + return joint_acc_score, F1_score, turn_acc_score + +def get_slot_information(ontology): + ontology_domains = dict([(k, v) for k, v in ontology.items() if k.split("-")[0] in EXPERIMENT_DOMAINS]) + SLOTS = [k.replace(" ","").lower() if ("book" not in k) else k.lower() for k in ontology_domains.keys()] + return SLOTS + +if __name__ == "__main__": + ontology = json.load(open("data/multi-woz/MULTIWOZ2 2/ontology.json", 'r')) + ALL_SLOTS = get_slot_information(ontology) + with open("save/t5/results/zeroshot_prediction.json") as f: + prediction = json.load(f) + + joint_acc_score, F1_score, turn_acc_score = evaluate_metrics(prediction, ontology) + + evaluation_metrics = {"Joint Acc":joint_acc_score, "Turn Acc":turn_acc_score, "Joint F1":F1_score} + print(evaluation_metrics) diff --git a/T5DST/figures/diagram.png b/T5DST/figures/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..153a80488f7ee347ddd16c17cca5be3be0705666 GIT binary patch literal 111242 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
    D59)saLtq|HvLlq|$LOQB__JWA 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μ$1SNqicwxnD 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Π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 literal 0 HcmV?d00001 diff --git a/T5DST/figures/slotdesc.png b/T5DST/figures/slotdesc.png new file mode 100644 index 0000000000000000000000000000000000000000..00137b1de5f3108010cbf5f68b37f40d96d610fb GIT binary patch literal 77736 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%0D6&jEG&pkc;(zvipLP}9 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 o_S*yfjdpF literal 0 HcmV?d00001 diff --git a/T5DST/train_GPT.py b/T5DST/train_GPT.py new file mode 100644 index 0000000..78a4118 --- /dev/null +++ b/T5DST/train_GPT.py @@ -0,0 +1,188 @@ +# Copyright (c) Facebook, Inc. and its affiliates + +import os, random +import torch +import argparse +import pytorch_lightning as pl +from pytorch_lightning import Trainer, seed_everything +from transformers import (AdamW, GPT2Tokenizer, GPT2LMHeadModel) +from data_loader import prepare_data +from config import get_args +from evaluate import evaluate_metrics +import json +from tqdm import tqdm +from copy import deepcopy +import numpy as np + + +def greedy_decode(input_text, tokenizer, model, args, max_length, current_output=None): + if current_output is None: + current_output = [] + + input_ids = tokenizer.encode(input_text, add_special_tokens=False) + with torch.no_grad(): + for i in range(max_length): + input_tensor = torch.tensor(input_ids+current_output, device=torch.device("cuda:0")).unsqueeze(0) + logits = model(input_tensor) + if isinstance(logits, tuple): # for gpt2 and maybe others + logits = logits[0] + predicted_index = torch.argmax(logits[0, -1, :]).item() + + if predicted_index==tokenizer.eos_token_id: + break + current_output.append(predicted_index) + + output_text = tokenizer.decode(current_output) + return output_text + + + + +class DST_GPT(pl.LightningModule): + + def __init__(self,args, tokenizer, model): + super().__init__() + self.tokenizer = tokenizer + self.model = model + self.lr = args["lr"] + self.args = args + + def training_step(self, batch, batch_idx): + self.model.train() + # follow https://github.com/salesforce/simpletod/blob/917f66afe7f37e75de246949423fc4470a2427c4/main.py#L148 + (loss), *_ = self.model(input_ids=batch["input_ids"], labels=batch["input_ids"]) + + return {'loss': loss, 'log': {'train_loss': loss}} + + + def validation_step(self, batch, batch_idx): + self.model.eval() + (loss), *_ = self.model(input_ids=batch["input_ids"], labels=batch["input_ids"]) + + return {'val_loss': loss, 'log': {'val_loss': loss}} + # return result + + def validation_epoch_end(self, outputs): + val_loss_mean = sum([o['val_loss'] for o in outputs]) / len(outputs) + # show val_loss in progress bar but only log val_loss + results = {'progress_bar': {'val_loss': val_loss_mean.item()}, 'log': {'val_loss': val_loss_mean.item()}, + 'val_loss': val_loss_mean.item()} + return results + + + def configure_optimizers(self): + return AdamW(self.parameters(), lr=self.lr, correct_bias=True) + + + +def train(args, *more): + # # train! + args = vars(args) + args["model_name"] = args["model_checkpoint"]+args["model_name"]+"_except_domain_"+args["except_domain"]+ "_slotlang_" +str(args["slot_lang"]) + "_lr_" +str(args["lr"]) + "_epoch_" + str(args["n_epochs"]) + "_seed_" + str(args["seed"]) + # train! + seed_everything(args["seed"]) + + model = GPT2LMHeadModel.from_pretrained(args["model_checkpoint"]) + tokenizer = GPT2Tokenizer.from_pretrained(args["model_checkpoint"], bos_token="[bos]", eos_token="[eos]", sep_token="[sep]", pad_token = "[pad]") + model.resize_token_embeddings(new_num_tokens=len(tokenizer)) + + task = DST_GPT(args, tokenizer, model) + train_loader, val_loader, test_loader, ALL_SLOTS, fewshot_loader_dev, fewshot_loader_test = prepare_data(args, task.tokenizer) + + #save model + save_path = os.path.join(args["saving_dir"],args["model_name"]) + if not os.path.exists(save_path): + os.makedirs(save_path) + + trainer = Trainer( + default_root_dir=save_path, + accumulate_grad_batches=args["gradient_accumulation_steps"], + gradient_clip_val=args["max_norm"], + max_epochs=args["n_epochs"], + callbacks=[pl.callbacks.EarlyStopping(monitor='val_loss',min_delta=0.00, patience=5,verbose=False, mode='min')], + gpus=args["GPU"], + deterministic=True, + num_nodes=1, + accelerator="ddp" + ) + + trainer.fit(task, train_loader, val_loader) + + + task.model.save_pretrained(save_path) + task.tokenizer.save_pretrained(save_path) + + print("test start...") + #evaluate model + _ = evaluate_model(args, task.tokenizer, task.model, test_loader, save_path, ALL_SLOTS) + + + +def evaluate_model(args, tokenizer, model, test_loader, save_path, ALL_SLOTS, prefix="zeroshot"): + save_path = os.path.join(save_path,"results") + if not os.path.exists(save_path): + os.makedirs(save_path) + predictions = {} + # to gpu + device = torch.device("cuda:0") + model.to(device) + model.eval() + + slot_logger = {slot_name:[0,0,0] for slot_name in ALL_SLOTS} + + for batch in tqdm(test_loader): + for idx, input_text in enumerate(batch["intput_text"]): + dst_text = greedy_decode(input_text, tokenizer, model, args, max_length=200) + value = dst_text.strip() + dial_id = batch["ID"][idx] + + if dial_id not in predictions: + predictions[dial_id] = {} + predictions[dial_id]["domain"] = batch["domains"][idx][0] + predictions[dial_id]["turns"] = {} + if batch["turn_id"][idx] not in predictions[dial_id]["turns"]: + predictions[dial_id]["turns"][batch["turn_id"][idx]] = {"turn_belief":batch["turn_belief"][idx], "pred_belief":[]} + + if value!="none": + predictions[dial_id]["turns"][batch["turn_id"][idx]]["pred_belief"].append(str(batch["slot_text"][idx])+'-'+str(value)) + + # dst_text = greedy_decode(input_text, tokenizer, model, args, max_length=200) + # slot_values = dst_text.strip() + # dial_id = batch["ID"][idx] + + # if dial_id not in predictions: + # predictions[dial_id] = {} + # predictions[dial_id]["domain"] = batch["domains"][idx][0] + # predictions[dial_id]["turns"] = {} + # if batch["turn_id"][idx] not in predictions[dial_id]["turns"]: + # predictions[dial_id]["turns"][batch["turn_id"][idx]] = {"turn_belief":batch["turn_belief"][idx], "pred_belief":[]} + # # print(slot_values) + # # print(slot_values.split(", ")) + # for slot_value in slot_values.split(", "): + # value = slot_value.split("-")[-1] + # # print(value) + # if value!="none": + # predictions[dial_id]["turns"][batch["turn_id"][idx]]["pred_belief"].append(slot_value) + + + with open(os.path.join(save_path, f"{prefix}_prediction.json"), 'w') as f: + json.dump(predictions,f, indent=4) + + joint_acc_score, F1_score, turn_acc_score = evaluate_metrics(predictions, ALL_SLOTS) + + evaluation_metrics = {"Joint Acc":joint_acc_score, "Turn Acc":turn_acc_score, "Joint F1":F1_score} + print(f"{prefix} result:",evaluation_metrics) + + with open(os.path.join(save_path, f"{prefix}_result.json"), 'w') as f: + json.dump(evaluation_metrics,f, indent=4) + + return predictions + + + +if __name__ == "__main__": + args = get_args() + train(args) + + + # evaluate() diff --git a/T5DST/utils/analysis.py b/T5DST/utils/analysis.py new file mode 100644 index 0000000..7329e97 --- /dev/null +++ b/T5DST/utils/analysis.py @@ -0,0 +1,124 @@ +# Copyright (c) Facebook, Inc. and its affiliates + +import os, random +import torch +import argparse +import pytorch_lightning as pl +from pytorch_lightning import Trainer, seed_everything +from transformers import (AdamW, T5Tokenizer, T5ForConditionalGeneration) +from data_loader import prepare_data +from config import get_args +from evaluate import evaluate_metrics +import json +from tqdm import tqdm +from copy import deepcopy +import numpy as np +from collections import Counter + +class DST_Seq2Seq(pl.LightningModule): + + def __init__(self,args, tokenizer, model): + super().__init__() + self.tokenizer = tokenizer + self.model = model + self.lr = args["lr"] + + + def training_step(self, batch, batch_idx): + self.model.train() + (loss), *_ = self.model(input_ids=batch["encoder_input"], + attention_mask=batch["attention_mask"], + lm_labels=batch["decoder_output"] + ) + + # result = pl.TrainResult(loss) + # result.log('train_loss', loss, on_epoch=True) + return {'loss': loss, 'log': {'train_loss': loss}} + # return result + + def validation_step(self, batch, batch_idx): + self.model.eval() + (loss), *_ = self.model(input_ids=batch["encoder_input"], + attention_mask=batch["attention_mask"], + lm_labels=batch["decoder_output"] + ) + + + return {'val_loss': loss, 'log': {'val_loss': loss}} + # return result + + def validation_epoch_end(self, outputs): + val_loss_mean = sum([o['val_loss'] for o in outputs]) / len(outputs) + # show val_loss in progress bar but only log val_loss + results = {'progress_bar': {'val_loss': val_loss_mean.item()}, 'log': {'val_loss': val_loss_mean.item()}, + 'val_loss': val_loss_mean.item()} + return results + + def configure_optimizers(self): + return AdamW(self.parameters(), lr=self.lr, correct_bias=True) + +def analysis(args): + args = vars(args) + # args["model_checkpoint"] = "trained/t5-smallt5_except_domain_train_slotlang_rule2_lr_0.0001_epoch_5_seed_555" + model = T5ForConditionalGeneration.from_pretrained(args["model_checkpoint"]) + tokenizer = T5Tokenizer.from_pretrained(args["model_checkpoint"], bos_token="[bos]", eos_token="[eos]", sep_token="[sep]") + model.resize_token_embeddings(new_num_tokens=len(tokenizer)) + train_loader, val_loader, test_loader, ALL_SLOTS, fewshot_loader_dev, fewshot_loader_test = prepare_data(args, tokenizer) + device = torch.device("cuda:0") + + # model.load_state_dict(torch.load("trained/t5-smallt5_except_domain_train_slotlang_none_lr_0.0001_epoch_5_seed_555/pytorch_model.bin")) + model.to(device) + model.eval() + count = 0 + for batch in test_loader: + decoder_input = torch.full((batch["encoder_input"].shape[0], 1), model.config.decoder_start_token_id, dtype=torch.long, device=device) + + # dst_outputs = model.generate(input_ids=batch["encoder_input"].to(device), + # attention_mask=batch["attention_mask"].to(device), + # eos_token_id=tokenizer.eos_token_id, + # max_length=200, + # ) + # if batch["value_text"][0]!="none": + # print(batch["intput_text"][0]) + # value_batch = tokenizer.batch_decode(dst_outputs, skip_special_tokens=True) + # print(value_batch) + outputs = model(input_ids=batch["encoder_input"].to(device), + attention_mask=batch["attention_mask"].to(device), + decoder_input_ids=decoder_input, + return_dict=True, + output_attentions=True, + ) + if batch["value_text"][0]!="none": + print(batch["intput_text"][0]) + tokens = tokenizer.convert_ids_to_tokens(batch["encoder_input"][0]) + max_id = torch.argmax(torch.sum(outputs.cross_attentions[1], 1).squeeze()).item() + + weights = torch.sum(outputs.cross_attentions[1], 1).squeeze().cpu().tolist() + bukets = [] + for i in range(len(tokens)): + bukets.append((tokens[i], weights[i])) + # bukets.sort(key=lambda x: x[1]) + print(bukets[max(max_id-1,0)]) + print(bukets[max_id]) + print(bukets[max_id+1]) + count+=1 + if count>30: + exit(0) + + + # print(batch["encoder_input"].shape) + # torch.sum(outputs.cross_attentions[0], 1).squeeze().cpu().tolist() + #print(torch.sum(outputs.cross_attentions[0], 1).squeeze().cpu().tolist()) + + #print(torch.sum(outputs.cross_attentions[1], 1).squeeze()) + + + + + + +if __name__ == "__main__": + args = get_args() + analysis(args) + +# python analysis.py --test_batch_size 1 --model_checkpoint trained/t5-smallt5_except_domain_train_slotlang_rule2_lr_0.0001_epoch_5_seed_555 --except_domain train --slot_lang rule2 diff --git a/T5DST/utils/fix_label.py b/T5DST/utils/fix_label.py new file mode 100644 index 0000000..d72bae3 --- /dev/null +++ b/T5DST/utils/fix_label.py @@ -0,0 +1,67 @@ +# Copyright (c) Facebook, Inc. and its affiliates + +# from TRADE +def fix_general_label_error(labels, slots): + label_dict = labels + GENERAL_TYPO = { + # type + "guesthouse":"guest house", "guesthouses":"guest house", "guest":"guest house", "mutiple sports":"multiple sports", + "sports":"multiple sports", "mutliple sports":"multiple sports","swimmingpool":"swimming pool", "concerthall":"concert hall", + "concert":"concert hall", "pool":"swimming pool", "night club":"nightclub", "mus":"museum", "ol":"architecture", + "colleges":"college", "coll":"college", "architectural":"architecture", "musuem":"museum", "churches":"church", + # area + "center":"centre", "center of town":"centre", "near city center":"centre", "in the north":"north", "cen":"centre", "east side":"east", + "east area":"east", "west part of town":"west", "ce":"centre", "town center":"centre", "centre of cambridge":"centre", + "city center":"centre", "the south":"south", "scentre":"centre", "town centre":"centre", "in town":"centre", "north part of town":"north", + "centre of town":"centre", "cb30aq": "none", + # price + "mode":"moderate", "moderate -ly": "moderate", "mo":"moderate", + # day + "next friday":"friday", "monda": "monday", + # parking + "free parking":"free", + # internet + "free internet":"yes", + # star + "4 star":"4", "4 stars":"4", "0 star rarting":"none", + # others + "y":"yes", "any":"dontcare", "n":"no", "does not care":"dontcare", "not men":"none", "not":"none", "not mentioned":"none", + '':"none", "not mendtioned":"none", "3 .":"3", "does not":"no", "fun":"none", "art":"none", + } + + for slot in slots: + if slot in label_dict.keys(): + # general typos + if label_dict[slot] in GENERAL_TYPO.keys(): + label_dict[slot] = label_dict[slot].replace(label_dict[slot], GENERAL_TYPO[label_dict[slot]]) + + # miss match slot and value + if slot == "hotel-type" and label_dict[slot] in ["nigh", "moderate -ly priced", "bed and breakfast", "centre", "venetian", "intern", "a cheap -er hotel"] or \ + slot == "hotel-internet" and label_dict[slot] == "4" or \ + slot == "hotel-pricerange" and label_dict[slot] == "2" or \ + slot == "attraction-type" and label_dict[slot] in ["gastropub", "la raza", "galleria", "gallery", "science", "m"] or \ + "area" in slot and label_dict[slot] in ["moderate"] or \ + "day" in slot and label_dict[slot] == "t": + label_dict[slot] = "none" + elif slot == "hotel-type" and label_dict[slot] in ["hotel with free parking and free wifi", "4", "3 star hotel"]: + label_dict[slot] = "hotel" + elif slot == "hotel-star" and label_dict[slot] == "3 star hotel": + label_dict[slot] = "3" + elif "area" in slot: + if label_dict[slot] == "no": label_dict[slot] = "north" + elif label_dict[slot] == "we": label_dict[slot] = "west" + elif label_dict[slot] == "cent": label_dict[slot] = "centre" + elif "day" in slot: + if label_dict[slot] == "we": label_dict[slot] = "wednesday" + elif label_dict[slot] == "no": label_dict[slot] = "none" + elif "price" in slot and label_dict[slot] == "ch": + label_dict[slot] = "cheap" + elif "internet" in slot and label_dict[slot] == "free": + label_dict[slot] = "yes" + + # some out-of-define classification slot values + if slot == "restaurant-area" and label_dict[slot] in ["stansted airport", "cambridge", "silver street"] or \ + slot == "attraction-area" and label_dict[slot] in ["norwich", "ely", "museum", "same area as hotel"]: + label_dict[slot] = "none" + + return label_dict diff --git a/T5DST/utils/generate_slot_desp.py b/T5DST/utils/generate_slot_desp.py new file mode 100644 index 0000000..3b8cb56 --- /dev/null +++ b/T5DST/utils/generate_slot_desp.py @@ -0,0 +1,55 @@ +# Copyright (c) Facebook, Inc. and its affiliates + +import json + +# EXPERIMENT_DOMAINS = ["hotel", "train", "restaurant", "attraction", "taxi"] + + +with open("slot_description.json", 'r') as f: + ontology = json.load(f) + + +slot_map = {"pricerange": "price range", "arriveby": "arrive by", "leaveat": "leave at"} +slot_types = {str(["book stay", "book people", "stars"]):"number of ", str(["parking", "internet"]):"whether have ", str(["destination", "departure"]):"location of ", str(["arriveby", "leaveat"]):"time of "} + + +# Naive descriptions +for domain_slot in ontology: + domain, slot = domain_slot.split("-") + if slot in slot_map: + slot = slot_map[slot] + if "book" in domain_slot: + slot = slot.replace("book ", "") + ontology[domain_slot]["naive"] = f"{slot} for the {domain} booking" + else: + ontology[domain_slot]["naive"] = f"{slot} of the {domain}" + + +# question +for domain_slot in ontology: + domain, slot = domain_slot.split("-") + ontology[domain_slot]["question"] = f"What is the {slot} of the {domain} that the user in interested in?" + + +# Slot Type +for domain_slot in ontology: + domain, slot = domain_slot.split("-") + slot_name = slot + if slot in slot_map: + slot_name = slot_map[slot] + prefix = "" + for slot_list, slot_type in slot_types.items(): + if slot in slot_list: + prefix = slot_type + + if "book" in domain_slot: + slot_name = slot_name.replace("book ", "") + ontology[domain_slot]["slottype"] = f"{prefix}{slot_name} for the {domain} booking" + elif prefix=="whether have ": + ontology[domain_slot]["slottype"] = f"{prefix}{slot_name} in the {domain}" + else: + ontology[domain_slot]["slottype"] = f"{prefix}{slot_name} of the {domain}" + + +with open('slot_description.json', 'w') as f: + json.dump(ontology, f, indent=4) diff --git a/T5DST/utils/mapping.pair b/T5DST/utils/mapping.pair new file mode 100644 index 0000000..34df41d --- /dev/null +++ b/T5DST/utils/mapping.pair @@ -0,0 +1,83 @@ +it's it is +don't do not +doesn't does not +didn't did not +you'd you would +you're you are +you'll you will +i'm i am +they're they are +that's that is +what's what is +couldn't could not +i've i have +we've we have +can't cannot +i'd i would +i'd i would +aren't are not +isn't is not +wasn't was not +weren't were not +won't will not +there's there is +there're there are +. . . +restaurants restaurant -s +hotels hotel -s +laptops laptop -s +cheaper cheap -er +dinners dinner -s +lunches lunch -s +breakfasts breakfast -s +expensively expensive -ly +moderately moderate -ly +cheaply cheap -ly +prices price -s +places place -s +venues venue -s +ranges range -s +meals meal -s +locations location -s +areas area -s +policies policy -s +children child -s +kids kid -s +kidfriendly kid friendly +cards card -s +upmarket expensive +inpricey cheap +inches inch -s +uses use -s +dimensions dimension -s +driverange drive range +includes include -s +computers computer -s +machines machine -s +families family -s +ratings rating -s +constraints constraint -s +pricerange price range +batteryrating battery rating +requirements requirement -s +drives drive -s +specifications specification -s +weightrange weight range +harddrive hard drive +batterylife battery life +businesses business -s +hours hour -s +one 1 +two 2 +three 3 +four 4 +five 5 +six 6 +seven 7 +eight 8 +nine 9 +ten 10 +eleven 11 +twelve 12 +anywhere any where +good bye goodbye diff --git a/T5DST/utils/requirements.txt b/T5DST/utils/requirements.txt new file mode 100644 index 0000000..e9e022e --- /dev/null +++ b/T5DST/utils/requirements.txt @@ -0,0 +1,3 @@ +torch==1.7.0 +transformers==3.1.0 +pytorch-lightning==1.0.3 diff --git a/T5DST/utils/slot_description.json b/T5DST/utils/slot_description.json new file mode 100644 index 0000000..0d1cc1e --- /dev/null +++ b/T5DST/utils/slot_description.json @@ -0,0 +1,344 @@ +{ + "hotel-pricerange": { + "description_human": "price budget of the hotel", + "values": [ + "cheap", + "dontcare", + "expensive", + "moderate" + ], + "naive": "price range of the hotel", + "question": "What is the pricerange of the hotel that the user in interested in?", + "slottype": "price range of the hotel" + }, + "hotel-type": { + "description_human": "what is the type of the hotel", + "values": [ + "guesthouse", + "hotel" + ], + "naive": "type of the hotel", + "question": "What is the type of the hotel that the user in interested in?", + "slottype": "type of the hotel" + }, + "hotel-parking": { + "description_human": "whether the hotel has parking", + "values": [ + "dontcare", + "free", + "no", + "yes" + ], + "naive": "parking of the hotel", + "question": "What is the parking of the hotel that the user in interested in?", + "slottype": "whether have parking in the hotel" + }, + "hotel-book stay": { + "description_human": "length of stay at the hotel", + "values": [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8" + ], + "naive": "stay for the hotel booking", + "question": "What is the book stay of the hotel that the user in interested in?", + "slottype": "number of stay for the hotel booking" + }, + "hotel-book day": { + "description_human": "day of the hotel booking", + "values": [ + "friday", + "monday", + "saturday", + "sunday", + "thursday", + "tuesday", + "wednesday" + ], + "naive": "day for the hotel booking", + "question": "What is the book day of the hotel that the user in interested in?", + "slottype": "day for the hotel booking" + }, + "hotel-book people": { + "description_human": "number of people for the hotel booking", + "values": [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8" + ], + "naive": "people for the hotel booking", + "question": "What is the book people of the hotel that the user in interested in?", + "slottype": "number of people for the hotel booking" + }, + "hotel-area": { + "description_human": "area or place of the hotel", + "values": [ + "centre", + "dontcare", + "east", + "north", + "south", + "west" + ], + "naive": "area of the hotel", + "question": "What is the area of the hotel that the user in interested in?", + "slottype": "area of the hotel" + }, + "hotel-stars": { + "description_human": "star rating of the hotel", + "values": [ + "0", + "1", + "2", + "3", + "4", + "5", + "dontcare" + ], + "naive": "stars of the hotel", + "question": "What is the stars of the hotel that the user in interested in?", + "slottype": "number of stars of the hotel" + }, + "hotel-internet": { + "description_human": "whether the hotel has internet", + "values": [ + "dontcare", + "no", + "yes" + ], + "naive": "internet of the hotel", + "question": "What is the internet of the hotel that the user in interested in?", + "slottype": "whether have internet in the hotel" + }, + "train-destination": { + "description_human": "destination of the train", + "values": [], + "naive": "destination of the train", + "question": "What is the destination of the train that the user in interested in?", + "slottype": "location of destination of the train" + }, + "train-day": { + "description_human": "day of the train", + "values": [ + "dontcare", + "friday", + "monday", + "saturday", + "sunday", + "thursday", + "tuesday", + "wednesday" + ], + "naive": "day of the train", + "question": "What is the day of the train that the user in interested in?", + "slottype": "day of the train" + }, + "train-departure": { + "description_human": "departure location of the train", + "values": [], + "naive": "departure of the train", + "question": "What is the departure of the train that the user in interested in?", + "slottype": "location of departure of the train" + }, + "train-arriveby": { + "description_human": "arrival time of the train", + "values": [], + "naive": "arrive by of the train", + "question": "What is the arriveby of the train that the user in interested in?", + "slottype": "time of arrive by of the train" + }, + "train-book people": { + "description_human": "how many train tickets you need", + "values": [ + "0", + "1", + "10", + "15", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9" + ], + "naive": "people for the train booking", + "question": "What is the book people of the train that the user in interested in?", + "slottype": "number of people for the train booking" + }, + "train-leaveat": { + "description_human": "leaving time for the train", + "values": [], + "naive": "leave at of the train", + "question": "What is the leaveat of the train that the user in interested in?", + "slottype": "time of leave at of the train" + }, + "attraction-area": { + "description_human": "area to search for attractions", + "values": [ + "cambridge", + "centre", + "dontcare", + "east", + "north", + "south", + "west" + ], + "naive": "area of the attraction", + "question": "What is the area of the attraction that the user in interested in?", + "slottype": "area of the attraction" + }, + "restaurant-food": { + "description_human": "the cuisine of the restaurant you are looking for", + "values": [], + "naive": "food of the restaurant", + "question": "What is the food of the restaurant that the user in interested in?", + "slottype": "food of the restaurant" + }, + "restaurant-pricerange": { + "description_human": "price budget for the restaurant", + "values": [ + "cheap", + "dontcare", + "expensive", + "moderate" + ], + "naive": "price range of the restaurant", + "question": "What is the pricerange of the restaurant that the user in interested in?", + "slottype": "price range of the restaurant" + }, + "restaurant-area": { + "description_human": "area or place of the restaurant", + "values": [ + "centre", + "east", + "north", + "south", + "west" + ], + "naive": "area of the restaurant", + "question": "What is the area of the restaurant that the user in interested in?", + "slottype": "area of the restaurant" + }, + "attraction-name": { + "description_human": "name of the attraction", + "values": [], + "naive": "name of the attraction", + "question": "What is the name of the attraction that the user in interested in?", + "slottype": "name of the attraction" + }, + "restaurant-name": { + "description_human": "name of the restaurant", + "values": [], + "naive": "name of the restaurant", + "question": "What is the name of the restaurant that the user in interested in?", + "slottype": "name of the restaurant" + }, + "attraction-type": { + "description_human": "type of the attraction", + "values": [ + "architecture", + "boat", + "church", + "cinema", + "college", + "concerthall", + "entertainment", + "hotspot", + "multiple sports", + "museum", + "nightclub", + "park", + "special", + "swimmingpool", + "theatre" + ], + "naive": "type of the attraction", + "question": "What is the type of the attraction that the user in interested in?", + "slottype": "type of the attraction" + }, + "hotel-name": { + "description_human": "name of the hotel", + "values": [], + "naive": "name of the hotel", + "question": "What is the name of the hotel that the user in interested in?", + "slottype": "name of the hotel" + }, + "taxi-leaveat": { + "description_human": "leaving time of taxi", + "values": [], + "naive": "leave at of the taxi", + "question": "What is the leaveat of the taxi that the user in interested in?", + "slottype": "time of leave at of the taxi" + }, + "taxi-destination": { + "description_human": "destination of taxi", + "values": [], + "naive": "destination of the taxi", + "question": "What is the destination of the taxi that the user in interested in?", + "slottype": "location of destination of the taxi" + }, + "taxi-departure": { + "description_human": "departure location of taxi", + "values": [], + "naive": "departure of the taxi", + "question": "What is the departure of the taxi that the user in interested in?", + "slottype": "location of departure of the taxi" + }, + "restaurant-book time": { + "description_human": "time of the restaurant booking", + "values": [], + "naive": "time for the restaurant booking", + "question": "What is the book time of the restaurant that the user in interested in?", + "slottype": "time for the restaurant booking" + }, + "restaurant-book day": { + "description_human": "day of the restaurant booking", + "values": [ + "friday", + "monday", + "saturday", + "sunday", + "thursday", + "tuesday", + "wednesday" + ], + "naive": "day for the restaurant booking", + "question": "What is the book day of the restaurant that the user in interested in?", + "slottype": "day for the restaurant booking" + }, + "restaurant-book people": { + "description_human": "how many people for the restaurant reservation", + "values": [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8" + ], + "naive": "people for the restaurant booking", + "question": "What is the book people of the restaurant that the user in interested in?", + "slottype": "number of people for the restaurant booking" + }, + "taxi-arriveby": { + "description_human": "arrival time of taxi", + "values": [], + "naive": "arrive by of the taxi", + "question": "What is the arriveby of the taxi that the user in interested in?", + "slottype": "time of arrive by of the taxi" + } +} \ No newline at end of file