Repository: huggingface/peft

Issue:
Deepcopy not copying the LoraConfig
I'm trying to make a deepcopy (using copy.deepcopy) of a LoraModel on the version '0.2.0'. But the values in PeftConfig remain the default ones (e.g. r = 8), not the ones of the copied model. Is it normal ? Am I supposed to do a `model_copy = get_peft_model(model_copy, peft_config)` ?
It is possible to implement `__deepcopy__` and `__copy__` if necessary.

Here is a reproduction example :
``` Python
import copy
from transformers import AutoModelForCausalLM
from peft import get_peft_config, get_peft_model, LoraConfig, TaskType
model_name_or_path = "gpt2"
tokenizer_name_or_path = "gpt2"
peft_config = LoraConfig(task_type=TaskType.CAUSAL_LM, inference_mode=False, r=87, lora_alpha=32, lora_dropout=0.1)
model = AutoModelForCausalLM.from_pretrained(model_name_or_path)
model = get_peft_model(model, peft_config)

model_copy = copy.deepcopy(model)
assert(model.peft_config.r == model_copy.peft_config.r)
```

Moreover, I also get an AssertionError if I continue with :
``` Python
model_copy = get_peft_model(model_copy, peft_config)
assert(model == model_copy)
```


Discussion:
Not stale, still a problem in the current version. 
Hello, the issue is because of the following property and methods of dataclass `__dict__` and `to_dict`:

![Screenshot 2023-06-27 at 5 04 30 PM](https://github.com/huggingface/peft/assets/13534540/a5132d0d-0039-47fd-851a-954bed12b014)

I will look int how to resolve this as and when I have time. cc @younesbelkada, @sgugger  and @muellerzr  in case you have ideas and already encountered this before 
Any idea why `__dict__` was overridden in the first place? Messing with `__dict__` is almost always a bad idea. Maybe we could just remove the `property` and replace the instances of `self.__dict__` further below by `asdict(self.__dict__)`?

Resolving patch:
diff --git a/src/peft/utils/config.py b/src/peft/utils/config.py
--- a/src/peft/utils/config.py
+++ b/src/peft/utils/config.py
@@ -55,12 +55,8 @@ class PeftConfigMixin(PushToHubMixin):
     """
     peft_type: Optional[PeftType] = field(default=None, metadata={"help": "The type of PEFT model."})
 
-    @property
-    def __dict__(self):
-        return asdict(self)
-
     def to_dict(self):
-        return self.__dict__
+        return asdict(self)
 
     def save_pretrained(self, save_directory, **kwargs):
         r"""
@@ -78,7 +74,7 @@ def save_pretrained(self, save_directory, **kwargs):
 
         os.makedirs(save_directory, exist_ok=True)
 
-        output_dict = self.__dict__
+        output_dict = asdict(self)
         output_path = os.path.join(save_directory, CONFIG_NAME)
 
         # save it


Test patch:
diff --git a/tests/test_config.py b/tests/test_config.py
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -12,7 +12,9 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+import copy
 import os
+import pickle
 import tempfile
 import unittest
 
@@ -97,11 +99,9 @@ def test_to_dict(self):
         r"""
         Test if the config can be correctly converted to a dict using:
         - to_dict
-        - __dict__
         """
         for config_class in self.all_config_classes:
             config = config_class()
-            self.assertEqual(config.to_dict(), config.__dict__)
             self.assertTrue(isinstance(config.to_dict(), dict))
 
     def test_from_pretrained_cache_dir(self):
@@ -133,3 +133,24 @@ def test_set_attributes(self):
 
                 config_from_pretrained = config_class.from_pretrained(tmp_dirname)
                 self.assertEqual(config.to_dict(), config_from_pretrained.to_dict())
+
+    def test_config_copy(self):
+        # see https://github.com/huggingface/peft/issues/424
+        for config_class in self.all_config_classes:
+            config = config_class()
+            copied = copy.copy(config)
+            self.assertEqual(config.to_dict(), copied.to_dict())
+
+    def test_config_deepcopy(self):
+        # see https://github.com/huggingface/peft/issues/424
+        for config_class in self.all_config_classes:
+            config = config_class()
+            copied = copy.deepcopy(config)
+            self.assertEqual(config.to_dict(), copied.to_dict())
+
+    def test_config_pickle_roundtrip(self):
+        # see https://github.com/huggingface/peft/issues/424
+        for config_class in self.all_config_classes:
+            config = config_class()
+            copied = pickle.loads(pickle.dumps(config))
+            self.assertEqual(config.to_dict(), copied.to_dict())